From 27641ebc0c722a82fe3456a97908cf0626acabdc Mon Sep 17 00:00:00 2001 From: isabell-ah Date: Tue, 29 Apr 2025 01:15:47 +0300 Subject: [PATCH 1/4] 1st commit --- .gitignore | 2 + package-lock.json | 1097 ++++++++++++++++++++++++++++++++++++++++ package.json | 28 +- server/grpc.js | 83 ++- server/index.js | 67 ++- server/invoice.js | 62 ++- server/rpc.proto | 77 +++ server/test-invoice.js | 12 + 8 files changed, 1373 insertions(+), 55 deletions(-) create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 server/rpc.proto create mode 100644 server/test-invoice.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13dfa36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4490e4f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1097 @@ +{ + "name": "lightning-app", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lightning-app", + "version": "1.0.0", + "dependencies": { + "@grpc/grpc-js": "^1.8.14", + "@grpc/proto-loader": "^0.7.7", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "express": "^4.17.1" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.3.tgz", + "integrity": "sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@types/node": { + "version": "22.15.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", + "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/protobufjs": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.0.tgz", + "integrity": "sha512-Z2E/kOY1QjoMlCytmexzYfDm/w5fKAiRwpSzGtdnXW1zC88Z2yXazHHrOtwCzn+7wSxyE8PYM4rvVcMphF9sOA==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/package.json b/package.json index 9bdafea..3c53b4c 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { - "name": "lightning-app", - "version": "1.0.0", - "main": "server/index.js", - "scripts": { - "start": "node server/index.js" - }, - "dependencies": { - "express": "^4.17.1", - "cors": "^2.8.5", - "dotenv": "^16.0.3", - "@grpc/grpc-js": "^1.8.14", - "@grpc/proto-loader": "^0.7.7" - } -} \ No newline at end of file + "name": "lightning-app", + "version": "1.0.0", + "main": "server/index.js", + "scripts": { + "start": "node server/index.js" + }, + "dependencies": { + "express": "^4.17.1", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "@grpc/grpc-js": "^1.8.14", + "@grpc/proto-loader": "^0.7.7" + } +} diff --git a/server/grpc.js b/server/grpc.js index 0a93aa1..bc85bcb 100644 --- a/server/grpc.js +++ b/server/grpc.js @@ -1,28 +1,83 @@ const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const fs = require('fs'); +const path = require('path'); require('dotenv').config(); -// Load lnd rpc.proto file -const packageDefinition = protoLoader.loadSync('rpc.proto', {}); -const lnrpc = grpc.loadPackageDefinition(packageDefinition).lnrpc; +// Proto file loading options +const options = { + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true, +}; +// Load proto file +const packageDefinition = protoLoader.loadSync( + path.join(__dirname, 'rpc.proto'), + options +); -// Load TLS cert -const lndCert = fs.readFileSync(process.env.LND_TLS_CERT); -const sslCreds = grpc.credentials.createSsl(lndCert); +// Load the proto package definition +const lightning = grpc.loadPackageDefinition(packageDefinition); -// Macaroon Auth -const macaroon = fs.readFileSync(process.env.LND_MACAROON).toString('hex'); +// Verify the loaded package +if (!lightning.lnrpc || !lightning.lnrpc.Lightning) { + throw new Error('Failed to load Lightning service from proto file'); +} + +// Load credentials +const tlsCert = fs.readFileSync(process.env.LND_CERT_PATH); +const sslCreds = grpc.credentials.createSsl(tlsCert); + +// Load macaroon +const macaroon = fs.readFileSync(process.env.LND_MACAROON_PATH).toString('hex'); const metadata = new grpc.Metadata(); metadata.add('macaroon', macaroon); -const macaroonCreds = grpc.credentials.createFromMetadataGenerator((_, callback) => { - callback(null, metadata); + +const macaroonCreds = grpc.credentials.createFromMetadataGenerator((_, cb) => { + cb(null, metadata); }); -// Create combined credentials -const creds = grpc.credentials.combineChannelCredentials(sslCreds, macaroonCreds); +// Combine credentials +const credentials = grpc.credentials.combineChannelCredentials( + sslCreds, + macaroonCreds +); + +// Create LND client +const lnrpcClient = new lightning.lnrpc.Lightning( + process.env.LND_GRPC_HOST, + credentials +); + +// Wrap the client methods in promises +const lnd = { + getInfo: () => { + return new Promise((resolve, reject) => { + lnrpcClient.getInfo({}, (err, response) => { + if (err) reject(err); + else resolve(response); + }); + }); + }, + addInvoice: (params) => { + return new Promise((resolve, reject) => { + lnrpcClient.addInvoice(params, (err, response) => { + if (err) reject(err); + else resolve(response); + }); + }); + }, + subscribeInvoices: (params) => { + return lnrpcClient.subscribeInvoices(params); + }, +}; -// Connect to lnd -const lnd = new lnrpc.Lightning(process.env.LND_GRPC_HOST, creds); +// Test connection +lnd + .getInfo() + .then((info) => console.log('Connected to LND:', info.alias)) + .catch((err) => console.error('Failed to connect to LND:', err)); module.exports = lnd; diff --git a/server/index.js b/server/index.js index 1ebed95..101c098 100644 --- a/server/index.js +++ b/server/index.js @@ -1,6 +1,8 @@ const express = require('express'); const { createInvoice, subscribeToInvoices } = require('./invoice'); const cors = require('cors'); +const util = require('util'); +const lnd = require('./grpc'); require('dotenv').config(); const app = express(); @@ -9,19 +11,64 @@ app.use(express.json()); const PORT = 3000; +// Health check endpoint +app.get('/health', (req, res) => { + res.json({ status: 'ok' }); +}); + +// Test LND connection +app.get('/test-connection', async (req, res) => { + try { + const getInfo = util.promisify(lnd.getInfo).bind(lnd); + const info = await getInfo({}); + res.json(info); + } catch (error) { + console.error('Connection test failed:', error); + res.status(500).json({ + error: 'Failed to connect to LND node', + details: error.message, + }); + } +}); + // Create Invoice endpoint app.post('/create-invoice', async (req, res) => { - const { amount } = req.body; - try { - const invoice = await createInvoice(amount); - res.json(invoice); - } catch (err) { - console.error(err); - res.status(500).send('Error creating invoice'); - } + console.log('Received request body:', req.body); + + const { amount } = req.body; + + if (!amount || isNaN(amount) || amount <= 0) { + return res.status(400).json({ + error: 'Invalid amount', + details: 'Amount must be a positive number', + }); + } + + try { + const invoice = await createInvoice(parseInt(amount)); + console.log('Created invoice:', invoice); + res.json(invoice); + } catch (err) { + console.error('Error creating invoice:', err); + res.status(500).json({ + error: 'Error creating invoice', + details: err.message, + }); + } }); app.listen(PORT, () => { - console.log(`Server listening at http://localhost:${PORT}`); - subscribeToInvoices(); // start payment listener + console.log(`Server listening at http://localhost:${PORT}`); + + // Start invoice subscription with retry mechanism + try { + subscribeToInvoices(); + } catch (error) { + console.error('Failed to start invoice subscription:', error); + } +}); + +// Handle uncaught errors +process.on('unhandledRejection', (error) => { + console.error('Unhandled Rejection:', error); }); diff --git a/server/invoice.js b/server/invoice.js index 5b77933..c6046c2 100644 --- a/server/invoice.js +++ b/server/invoice.js @@ -2,29 +2,57 @@ const lnd = require('./grpc'); // Create Invoice async function createInvoice(amountSats) { - return new Promise((resolve, reject) => { - const request = { - memo: "Test Invoice", - value: amountSats - }; - lnd.AddInvoice(request, (err, response) => { - if (err) { - return reject(err); - } - resolve(response); - }); - }); + try { + const request = { + value: amountSats.toString(), + memo: 'Test Invoice', + }; + if (!amountSats || amountSats <= 0) { + throw new Error('Invalid amount: must be greater than 0'); + } + + const response = await lnd.addInvoice(request); + console.log('Creating invoice with request:', request); + const invoice = { + payment_request: response.payment_request, + r_hash: Buffer.from(response.r_hash).toString('hex'), + add_index: response.add_index?.toString(), + amount: amountSats, + }; + + console.log('Successfully created invoice:', invoice); + return invoice; + } catch (error) { + console.error('Error creating invoice:', error); + throw error; + } } // Subscribe to Invoices function subscribeToInvoices() { - const call = lnd.SubscribeInvoices({}); + try { + const call = lnd.subscribeInvoices({}); + call.on('data', (invoice) => { - if (invoice.settled) { - console.log(`Invoice settled! ${invoice.memo}`); - } + if (invoice.settled) { + console.log('Invoice settled!', { + payment_request: invoice.payment_request, + value: invoice.value, + memo: invoice.memo, + }); + } + }); + + call.on('error', (error) => { + console.error('Invoice subscription error:', error); + }); + + call.on('end', () => { + console.log('Invoice subscription ended'); }); - call.on('error', console.error); + } catch (error) { + console.error('Error setting up invoice subscription:', error); + } } module.exports = { createInvoice, subscribeToInvoices }; diff --git a/server/rpc.proto b/server/rpc.proto new file mode 100644 index 0000000..3f32170 --- /dev/null +++ b/server/rpc.proto @@ -0,0 +1,77 @@ +syntax = "proto3"; + +package lnrpc; + +service Lightning { + rpc GetInfo (GetInfoRequest) returns (GetInfoResponse); + rpc AddInvoice (Invoice) returns (AddInvoiceResponse); + rpc SubscribeInvoices (InvoiceSubscription) returns (stream Invoice); +} + +message GetInfoRequest {} + +message GetInfoResponse { + string version = 1; + string identity_pubkey = 2; + string alias = 3; + uint32 num_active_channels = 4; + uint32 num_peers = 5; + uint32 block_height = 6; + string network = 7; +} + +message Invoice { + string memo = 1; + bytes r_preimage = 2; + bytes r_hash = 3; + string value = 4; + bool settled = 5; + int64 creation_date = 6; + int64 settle_date = 7; + string payment_request = 8; + string description_hash = 9; + int64 expiry = 10; + string fallback_addr = 11; + int64 cltv_expiry = 12; + repeated RouteHint route_hints = 13; + bool private = 14; + int64 add_index = 15; + int64 settle_index = 16; + int64 amt_paid = 17; + bytes payment_addr = 18; + map features = 19; + string state = 20; + bool is_keysend = 21; + bytes payment_request_hash = 22; + bool is_amp = 23; +} + +message InvoiceSubscription { + uint64 add_index = 1; + uint64 settle_index = 2; +} + +message AddInvoiceResponse { + bytes r_hash = 1; + string payment_request = 2; + uint64 add_index = 3; + bytes payment_addr = 4; +} + +message RouteHint { + repeated HopHint hop_hints = 1; +} + +message HopHint { + string node_id = 1; + uint32 chan_id = 2; + int64 fee_base_msat = 3; + int64 fee_proportional_millionths = 4; + uint32 cltv_expiry_delta = 5; +} + +message Feature { + string name = 1; + bool is_required = 2; + bool is_known = 3; +} diff --git a/server/test-invoice.js b/server/test-invoice.js new file mode 100644 index 0000000..e4f26d8 --- /dev/null +++ b/server/test-invoice.js @@ -0,0 +1,12 @@ +const { createInvoice } = require('./invoice'); + +async function test() { + try { + const invoice = await createInvoice(900); + console.log('Created invoice:', invoice); + } catch (error) { + console.error('Error:', error); + } +} +ss; +test(); From e02d25bdf6993c72912414a031f23440d30489f9 Mon Sep 17 00:00:00 2001 From: isabell-ah Date: Tue, 29 Apr 2025 01:29:24 +0300 Subject: [PATCH 2/4] 3rd commit --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 13dfa36..d296706 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env -node_modules/ \ No newline at end of file +node_modules/ +package-lock.json \ No newline at end of file From 2211391c984a3ce8e84552bb0e0bd4cf229fb3a2 Mon Sep 17 00:00:00 2001 From: isabell-ah Date: Tue, 29 Apr 2025 01:44:11 +0300 Subject: [PATCH 3/4] add invoice image --- READ.md | 20 ++++++++++++-------- client/image.png | Bin 0 -> 29431 bytes 2 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 client/image.png diff --git a/READ.md b/READ.md index 7cec1d3..5d2a93b 100644 --- a/READ.md +++ b/READ.md @@ -2,15 +2,19 @@ # Project Structure ``` + /lightning-app ├── /server -│ ├── index.js # Express app -│ ├── grpc.js # lnd gRPC client setup -│ ├── invoice.js # Invoice creation/listener +│ ├── index.js # Express app +│ ├── grpc.js # lnd gRPC client setup +│ ├── invoice.js # Invoice creation/listener ├── /client -│ ├── index.html # Basic frontend -│ ├── app.js # Fetch invoice, WebSocket events -├── .env.example # Connection variables -├── package.json # Node.js dependencies -├── README.md # Setup instructions +│ ├── index.html # Basic frontend +│ ├── app.js # Fetch invoice, WebSocket events +├── .env.example # Connection variables +├── package.json # Node.js dependencies +├── README.md # Setup instructions + +``` + ``` diff --git a/client/image.png b/client/image.png new file mode 100644 index 0000000000000000000000000000000000000000..126eab737e0c6e48c7d9d6917821ee851792aa0a GIT binary patch literal 29431 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 Date: Tue, 29 Apr 2025 21:09:07 +0300 Subject: [PATCH 4/4] minor changes --- .gitignore | 2 +- client/index.html | 15 +++++++------- server/index.js | 14 +++++-------- server/invoice.js | 50 +++++++++++++++++++---------------------------- 4 files changed, 33 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index d296706..28a76fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .env node_modules/ -package-lock.json \ No newline at end of file +package-lock.json diff --git a/client/index.html b/client/index.html index ff3c2b2..2069c6f 100644 --- a/client/index.html +++ b/client/index.html @@ -1,18 +1,17 @@ - - - + + Lightning Payment App - + - +

Pay with Lightning!

+
- - - \ No newline at end of file + + diff --git a/server/index.js b/server/index.js index 101c098..675743d 100644 --- a/server/index.js +++ b/server/index.js @@ -1,7 +1,6 @@ const express = require('express'); const { createInvoice, subscribeToInvoices } = require('./invoice'); const cors = require('cors'); -const util = require('util'); const lnd = require('./grpc'); require('dotenv').config(); @@ -16,17 +15,16 @@ app.get('/health', (req, res) => { res.json({ status: 'ok' }); }); -// Test LND connection +// test-connection app.get('/test-connection', async (req, res) => { try { - const getInfo = util.promisify(lnd.getInfo).bind(lnd); - const info = await getInfo({}); + const info = await lnd.getInfo(); res.json(info); - } catch (error) { - console.error('Connection test failed:', error); + } catch (err) { + console.error('Connection test failed:', err); res.status(500).json({ error: 'Failed to connect to LND node', - details: error.message, + details: err.message, }); } }); @@ -46,7 +44,6 @@ app.post('/create-invoice', async (req, res) => { try { const invoice = await createInvoice(parseInt(amount)); - console.log('Created invoice:', invoice); res.json(invoice); } catch (err) { console.error('Error creating invoice:', err); @@ -67,7 +64,6 @@ app.listen(PORT, () => { console.error('Failed to start invoice subscription:', error); } }); - // Handle uncaught errors process.on('unhandledRejection', (error) => { console.error('Unhandled Rejection:', error); diff --git a/server/invoice.js b/server/invoice.js index c6046c2..c966263 100644 --- a/server/invoice.js +++ b/server/invoice.js @@ -3,56 +3,46 @@ const lnd = require('./grpc'); // Create Invoice async function createInvoice(amountSats) { try { + if (!amountSats || amountSats <= 0) { + throw new Error('Invalid amount: must be greater than 0'); + } + // Create invoice request const request = { value: amountSats.toString(), memo: 'Test Invoice', }; - if (!amountSats || amountSats <= 0) { - throw new Error('Invalid amount: must be greater than 0'); - } + console.log('Creating invoice with request:', request); + // send request to LND const response = await lnd.addInvoice(request); - console.log('Creating invoice with request:', request); const invoice = { payment_request: response.payment_request, r_hash: Buffer.from(response.r_hash).toString('hex'), add_index: response.add_index?.toString(), amount: amountSats, }; - console.log('Successfully created invoice:', invoice); return invoice; } catch (error) { - console.error('Error creating invoice:', error); + console.error('Error creating invoice:', { + message: error.message, + stack: error.stack, + }); throw error; } } -// Subscribe to Invoices +// Subscribe to invoice updates function subscribeToInvoices() { - try { - const call = lnd.subscribeInvoices({}); - - call.on('data', (invoice) => { - if (invoice.settled) { - console.log('Invoice settled!', { - payment_request: invoice.payment_request, - value: invoice.value, - memo: invoice.memo, - }); - } - }); - - call.on('error', (error) => { - console.error('Invoice subscription error:', error); - }); - - call.on('end', () => { - console.log('Invoice subscription ended'); - }); - } catch (error) { - console.error('Error setting up invoice subscription:', error); - } + const call = lnd.subscribeInvoices({}); + call.on('data', (invoice) => { + if (invoice.settled) { + console.log( + `Invoice settled! ${(invoice.payment_request, invoice.memo)}` + ); + } + }); + call.on('Invoice subscription error', console.error); } module.exports = { createInvoice, subscribeToInvoices };