From 4bd4c081449f90d82378c7b359856b6b25c2396c Mon Sep 17 00:00:00 2001 From: Skinny001 Date: Wed, 25 Mar 2026 10:34:22 +0100 Subject: [PATCH 1/4] feat: implement payouts ledger (table, endpoint) for creator payout history --- index.js | 2 + node_modules/.package-lock.json | 1105 ++++++++++++------------------- package-lock.json | 334 ++++++++++ package.json | 1 + routes/payouts.js | 16 + src/db/appDatabase.js | 273 ++------ 6 files changed, 854 insertions(+), 877 deletions(-) create mode 100644 routes/payouts.js diff --git a/index.js b/index.js index bcd7ffb..f4eaa9d 100644 --- a/index.js +++ b/index.js @@ -42,6 +42,8 @@ function createApp(dependencies = {}) { app.use(express.json()); // Subscription events webhook app.use('/api/subscription', require('./routes/subscription')); + // Payouts API + app.use('/api/payouts', require('./routes/payouts')); app.get('/', (req, res) => { res.json({ diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index a51fae4..4996434 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -1062,25 +1062,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", - "node_modules/@noble/curves": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", - "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.8.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "license": "MIT", "dependencies": { "@noble/hashes": "2.0.1" @@ -1178,58 +1159,6 @@ "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-5.1.0.tgz", "integrity": "sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==", "license": "Apache-2.0 OR MIT", - "node_modules/@stellar/js-xdr": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz", - "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==", - "license": "Apache-2.0" - }, - "node_modules/@stellar/stellar-base": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-14.1.0.tgz", - "integrity": "sha512-A8kFli6QGy22SRF45IjgPAJfUNGjnI+R7g4DF5NZYVsD1kGf7B4ITyc4OPclLV9tqNI4/lXxafGEw0JEUbHixw==", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.9.6", - "@stellar/js-xdr": "^3.1.2", - "base32.js": "^0.1.0", - "bignumber.js": "^9.3.1", - "buffer": "^6.0.3", - "sha.js": "^2.4.12" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@stellar/stellar-sdk": { - "version": "14.6.1", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-14.6.1.tgz", - "integrity": "sha512-A1rQWDLdUasXkMXnYSuhgep+3ZZzyuXJKdt5/KAIc0gkmSp906HTvUpbT4pu+bVr41tu0+J4Ugz9J4BQAGGytg==", - "license": "Apache-2.0", - "dependencies": { - "@stellar/stellar-base": "^14.1.0", - "axios": "^1.13.3", - "bignumber.js": "^9.3.1", - "commander": "^14.0.2", - "eventsource": "^2.0.2", - "feaxios": "^0.0.23", - "randombytes": "^2.1.0", - "toml": "^3.0.0", - "urijs": "^1.19.1" - }, - "bin": { - "stellar-js": "bin/stellar-js" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", "dependencies": { "multiformats": "^13.0.0" } @@ -1566,24 +1495,15 @@ "license": "(Apache-2.0 AND MIT)" }, "node_modules/@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", "license": "MIT", "dependencies": { - "@noble/hashes": "1.3.2" + "@noble/hashes": "1.8.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/curves/node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "license": "MIT", "engines": { - "node": ">= 16" + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -1733,23 +1653,20 @@ "license": "Apache-2.0" }, "node_modules/@stellar/stellar-base": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-13.1.0.tgz", - "integrity": "sha512-90EArG+eCCEzDGj3OJNoCtwpWDwxjv+rs/RNPhvg4bulpjN/CSRj+Ys/SalRcfM4/WRC5/qAfjzmJBAuquWhkA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-14.1.0.tgz", + "integrity": "sha512-A8kFli6QGy22SRF45IjgPAJfUNGjnI+R7g4DF5NZYVsD1kGf7B4ITyc4OPclLV9tqNI4/lXxafGEw0JEUbHixw==", "license": "Apache-2.0", "dependencies": { + "@noble/curves": "^1.9.6", "@stellar/js-xdr": "^3.1.2", "base32.js": "^0.1.0", - "bignumber.js": "^9.1.2", + "bignumber.js": "^9.3.1", "buffer": "^6.0.3", - "sha.js": "^2.3.6", - "tweetnacl": "^1.0.3" + "sha.js": "^2.4.12" }, "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "sodium-native": "^4.3.3" + "node": ">=20.0.0" } }, "node_modules/@stellar/stellar-sdk": { @@ -1775,38 +1692,6 @@ "node": ">=20.0.0" } }, - "node_modules/@stellar/stellar-sdk/node_modules/@noble/curves": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", - "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.8.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/@stellar/stellar-base": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-14.1.0.tgz", - "integrity": "sha512-A8kFli6QGy22SRF45IjgPAJfUNGjnI+R7g4DF5NZYVsD1kGf7B4ITyc4OPclLV9tqNI4/lXxafGEw0JEUbHixw==", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.9.6", - "@stellar/js-xdr": "^3.1.2", - "base32.js": "^0.1.0", - "bignumber.js": "^9.3.1", - "buffer": "^6.0.3", - "sha.js": "^2.4.12" - }, - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1908,12 +1793,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "undici-types": "~6.19.2" } }, "node_modules/@types/normalize-package-data": { @@ -2056,8 +1941,6 @@ }, "node_modules/accepts": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -2407,6 +2290,20 @@ "node": ">=6.0.0" } }, + "node_modules/better-sqlite3": { + "version": "12.8.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.8.0.tgz", + "integrity": "sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x" + } + }, "node_modules/bignumber.js": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", @@ -2429,6 +2326,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -2485,8 +2391,6 @@ }, "node_modules/body-parser": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -2629,8 +2533,6 @@ }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2656,8 +2558,6 @@ }, "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==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2669,8 +2569,6 @@ }, "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==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2720,9 +2618,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001780", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", - "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", + "version": "1.0.30001781", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", + "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", "dev": true, "funding": [ { @@ -2847,6 +2745,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -3013,8 +2917,6 @@ }, "node_modules/content-disposition": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", "engines": { "node": ">=18" @@ -3026,8 +2928,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==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3042,8 +2942,6 @@ }, "node_modules/cookie": { "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3051,8 +2949,6 @@ }, "node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", "engines": { "node": ">=6.6.0" @@ -3073,8 +2969,6 @@ }, "node_modules/cors": { "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -3146,8 +3040,6 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3195,6 +3087,21 @@ "node": ">=0.10.0" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", @@ -3210,6 +3117,15 @@ } } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -3248,13 +3164,20 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -3300,8 +3223,6 @@ }, "node_modules/dotenv": { "version": "17.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", - "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -3312,8 +3233,6 @@ }, "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==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -3335,58 +3254,12 @@ }, "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==", "license": "MIT" }, "node_modules/electron-fetch": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/electron-fetch/-/electron-fetch-1.9.1.tgz", "integrity": "sha512-M9qw6oUILGVrcENMSRRefE1MbHPIz0h79EKIeJWK9v563aT9Qkh8aEHPO1H5vi970wPirNY+jO9OpFoLiMsMGA==", - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", - "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.11", - "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, "license": "MIT", "dependencies": { "encoding": "^0.1.13" @@ -3396,9 +3269,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.321", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", - "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", + "version": "1.5.322", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.322.tgz", + "integrity": "sha512-vFU34OcrvMcH66T+dYC3G4nURmgfDVewMIu6Q2urXpumAPSMmzvcn04KVVV8Opikq8Vs5nUbO/8laNhNRqSzYw==", "dev": true, "license": "ISC" }, @@ -3424,8 +3297,6 @@ }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3452,6 +3323,15 @@ "node": ">=0.10.0" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/err-code": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/err-code/-/err-code-3.0.1.tgz", @@ -3469,55 +3349,13 @@ }, "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==", - "license": "MIT", - "node_modules/base32.js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", - "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.10", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", - "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -3525,21 +3363,6 @@ }, "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==", - "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3575,8 +3398,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==", "license": "MIT" }, "node_modules/escape-string-regexp": { @@ -3605,8 +3426,6 @@ }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3640,6 +3459,18 @@ "node": ">=14.0.0" } }, + "node_modules/ethers/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/ethers/node_modules/@noble/hashes": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", @@ -3652,50 +3483,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/ethers/node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/ethers/node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -3715,28 +3502,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", "license": "MIT", "engines": { "node": ">=0.8.x" @@ -3784,6 +3549,15 @@ "node": ">= 0.8.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", @@ -3803,8 +3577,6 @@ }, "node_modules/express": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -3883,6 +3655,12 @@ "is-retry-allowed": "^3.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/files-from-path": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/files-from-path/-/files-from-path-0.2.6.tgz", @@ -3966,8 +3744,6 @@ }, "node_modules/finalhandler": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -3989,17 +3765,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -4019,20 +3784,6 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], - "node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=4.0" @@ -4115,8 +3866,6 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4124,13 +3873,17 @@ }, "node_modules/fresh": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4155,8 +3908,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==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4193,8 +3944,6 @@ }, "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==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4226,27 +3975,6 @@ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { "node": ">=8.0.0" @@ -4254,8 +3982,6 @@ }, "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==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -4278,6 +4004,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4315,8 +4047,6 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -4393,8 +4123,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -4426,8 +4154,6 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4441,11 +4167,6 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "license": "ISC", - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", "dependencies": { "lru-cache": "^6.0.0" }, @@ -4480,8 +4201,6 @@ }, "node_modules/http-errors": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -4510,8 +4229,6 @@ }, "node_modules/iconv-lite": { "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -4561,19 +4278,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "node_modules/eventsource": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", - "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "license": "MIT", "dependencies": { @@ -4623,8 +4327,12 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, "node_modules/interface-blockstore": { @@ -4670,21 +4378,6 @@ "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-5.1.0.tgz", "integrity": "sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==", "license": "Apache-2.0 OR MIT", - "node_modules/feaxios": { - "version": "0.0.23", - "resolved": "https://registry.npmjs.org/feaxios/-/feaxios-0.0.23.tgz", - "integrity": "sha512-eghR0A21fvbkcQBgZuMfQhrXxJzC0GNUGC9fXhBge33D+mFDTwl0aJ35zoQQn575BhyjQitRc5N4f+L4cP708g==", - "license": "MIT", - "dependencies": { - "is-retry-allowed": "^3.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", "dependencies": { "multiformats": "^13.0.0" } @@ -4706,8 +4399,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -4761,46 +4452,6 @@ "resolved": "https://registry.npmjs.org/blob-to-it/-/blob-to-it-1.0.4.tgz", "integrity": "sha512-iCmk0W4NdbrWgRRuxOriU8aM5ijeVLI61Zulsmg/lUHNr7pYjoj+U77opLefNagevtrrbMt3JQ5Qip7ar178kA==", "license": "ISC", - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", "dependencies": { "browser-readablestream-to-it": "^1.0.3" } @@ -4815,10 +4466,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/interface-datastore/-/interface-datastore-6.1.1.tgz", "integrity": "sha512-AmCS+9CT34pp2u0QQVXjKztkuq3y5T+BIciuiHDDtDZucZD8VudosnSdUyXJV6IsRkN5jc4RFDhCk1O6Q3Gxjg==", - "node_modules/form-data/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==", "license": "MIT", "dependencies": { "interface-store": "^2.0.2", @@ -4838,11 +4485,6 @@ "integrity": "sha512-sbRZA1QX3xJ6ywTiVQZMOxhlhp4osAZX2SXx3azOLxAtxmGWDMkHYt722VV4nZ2GyJy8qyk5GHQIZ0uvQnpaTg==", "deprecated": "js-IPFS has been deprecated in favour of Helia - please see https://github.com/ipfs/js-ipfs/issues/4336 for details", "license": "(Apache-2.0 OR MIT)", - "node_modules/form-data/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==", - "license": "MIT", "dependencies": { "interface-datastore": "^6.0.2", "multiaddr": "^10.0.0", @@ -5415,34 +5057,18 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/ipfs-utils/node_modules/native-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/native-fetch/-/native-fetch-3.0.0.tgz", - "integrity": "sha512-G3Z7vx0IFb/FQ4JxvtqGABsOTIqRWvgQz6e+erkB+JJD6LrszQtMozEHI4EkmgZQvnGHrpLVzUWk7t4sJCIkVw==", - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "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==", + "node_modules/ipfs-utils/node_modules/native-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/native-fetch/-/native-fetch-3.0.0.tgz", + "integrity": "sha512-G3Z7vx0IFb/FQ4JxvtqGABsOTIqRWvgQz6e+erkB+JJD6LrszQtMozEHI4EkmgZQvnGHrpLVzUWk7t4sJCIkVw==", "license": "MIT", "peerDependencies": { "node-fetch": "*" @@ -5453,11 +5079,6 @@ "resolved": "https://registry.npmjs.org/ipns/-/ipns-10.1.3.tgz", "integrity": "sha512-b2Zeh8+7qOV11NjnTsYLpG8K6T13uBMndpzk9N9E2Qjz/u80qsxvKpspSP32sErOLr/GWjdFVVc02E9PMojQNA==", "license": "Apache-2.0 OR MIT", - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", "dependencies": { "@libp2p/crypto": "^5.0.0", "@libp2p/interface": "^3.0.2", @@ -5592,31 +5213,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -5658,29 +5254,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "engines": { @@ -5752,8 +5325,6 @@ }, "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, "node_modules/is-regex": { @@ -7007,8 +6578,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -7016,8 +6585,6 @@ }, "node_modules/media-typer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -7061,19 +6628,8 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { "node": ">=18" @@ -7140,8 +6696,6 @@ }, "node_modules/mime-db": { "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -7149,8 +6703,6 @@ }, "node_modules/mime-types": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -7173,6 +6725,18 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -7238,6 +6802,12 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/move-file": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/move-file/-/move-file-2.1.0.tgz", @@ -7264,8 +6834,6 @@ }, "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==", "license": "MIT" }, "node_modules/multer": { @@ -7427,6 +6995,12 @@ "node": "^14 || ^16 || >=18" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, "node_modules/native-abort-controller": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/native-abort-controller/-/native-abort-controller-1.0.4.tgz", @@ -7454,13 +7028,35 @@ }, "node_modules/negotiator": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" } }, + "node_modules/node-abi": { + "version": "3.89.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", + "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -7482,9 +7078,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -7544,9 +7140,9 @@ } }, "node_modules/nodemon/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7660,8 +7256,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==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7669,8 +7263,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -7681,8 +7273,6 @@ }, "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==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -7693,8 +7283,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -7866,8 +7454,6 @@ }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -7910,8 +7496,6 @@ }, "node_modules/path-to-regexp": { "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", "funding": { "type": "opencollective", @@ -7925,9 +7509,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -7969,6 +7553,33 @@ "node": ">= 0.4" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -8075,8 +7686,6 @@ }, "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==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -8099,6 +7708,16 @@ "dev": true, "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -8118,8 +7737,6 @@ }, "node_modules/qs": { "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -8168,8 +7785,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -8177,8 +7792,6 @@ }, "node_modules/raw-body": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -8190,6 +7803,30 @@ "node": ">= 0.10" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -8434,8 +8071,6 @@ }, "node_modules/router": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -8487,8 +8122,6 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/semver": { @@ -8503,8 +8136,6 @@ }, "node_modules/send": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -8529,8 +8160,6 @@ }, "node_modules/serve-static": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -8565,8 +8194,6 @@ }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/sha.js": { @@ -8614,8 +8241,6 @@ }, "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==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8633,8 +8258,6 @@ }, "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==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8649,8 +8272,6 @@ }, "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==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -8667,8 +8288,6 @@ }, "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==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -8691,6 +8310,51 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -8825,8 +8489,6 @@ }, "node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -8852,6 +8514,26 @@ "node": ">=18.0.0" } }, + "node_modules/stellar-sdk/node_modules/@stellar/stellar-base": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-13.1.0.tgz", + "integrity": "sha512-90EArG+eCCEzDGj3OJNoCtwpWDwxjv+rs/RNPhvg4bulpjN/CSRj+Ys/SalRcfM4/WRC5/qAfjzmJBAuquWhkA==", + "license": "Apache-2.0", + "dependencies": { + "@stellar/js-xdr": "^3.1.2", + "base32.js": "^0.1.0", + "bignumber.js": "^9.1.2", + "buffer": "^6.0.3", + "sha.js": "^2.3.6", + "tweetnacl": "^1.0.3" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "sodium-native": "^4.3.3" + } + }, "node_modules/stream-to-it": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/stream-to-it/-/stream-to-it-0.2.4.tgz", @@ -8975,37 +8657,36 @@ } }, "node_modules/superagent": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", - "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", - "deprecated": "Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", + "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", "dev": true, "license": "MIT", "dependencies": { - "component-emitter": "^1.3.0", + "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", - "debug": "^4.3.4", + "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^3.5.1", + "form-data": "^4.0.5", + "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0" + "qs": "^6.14.1" }, "engines": { "node": ">=14.18.0" } }, "node_modules/supertest": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", - "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", - "deprecated": "Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", "dev": true, "license": "MIT", "dependencies": { + "cookie-signature": "^1.2.2", "methods": "^1.1.2", - "superagent": "^9.0.1" + "superagent": "^10.3.0" }, "engines": { "node": ">=14.18.0" @@ -9036,6 +8717,69 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/tar-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -9111,8 +8855,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=0.6" @@ -9155,6 +8897,18 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", @@ -9186,8 +8940,6 @@ }, "node_modules/type-is": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -9306,15 +9058,13 @@ } }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -9415,8 +9165,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -9642,8 +9390,6 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/write-file-atomic": { @@ -9727,6 +9473,15 @@ } }, "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/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==", diff --git a/package-lock.json b/package-lock.json index 2d109d4..0f2fc87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@stellar/stellar-sdk": "^14.6.1", "axios": "^1.6.2", + "better-sqlite3": "^12.8.0", "cors": "^2.8.6", "dotenv": "^17.3.1", "ethers": "^6.8.1", @@ -2313,6 +2314,20 @@ "node": ">=6.0.0" } }, + "node_modules/better-sqlite3": { + "version": "12.8.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.8.0.tgz", + "integrity": "sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x" + } + }, "node_modules/bignumber.js": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", @@ -2335,6 +2350,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -2745,6 +2769,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -3081,6 +3111,21 @@ "node": ">=0.10.0" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", @@ -3096,6 +3141,15 @@ } } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -3139,6 +3193,15 @@ "node": ">= 0.8" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -3284,6 +3347,15 @@ "node": ">=0.10.0" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/err-code": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/err-code/-/err-code-3.0.1.tgz", @@ -3501,6 +3573,15 @@ "node": ">= 0.8.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", @@ -3598,6 +3679,12 @@ "is-retry-allowed": "^3.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/files-from-path": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/files-from-path/-/files-from-path-0.2.6.tgz", @@ -3815,6 +3902,12 @@ "node": ">= 0.8" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3935,6 +4028,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4254,6 +4353,12 @@ "version": "2.0.4", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, "node_modules/interface-blockstore": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/interface-blockstore/-/interface-blockstore-2.0.3.tgz", @@ -6644,6 +6749,18 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -6709,6 +6826,12 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/move-file": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/move-file/-/move-file-2.1.0.tgz", @@ -6896,6 +7019,12 @@ "node": "^14 || ^16 || >=18" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, "node_modules/native-abort-controller": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/native-abort-controller/-/native-abort-controller-1.0.4.tgz", @@ -6928,6 +7057,30 @@ "node": ">= 0.6" } }, + "node_modules/node-abi": { + "version": "3.89.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", + "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -7424,6 +7577,33 @@ "node": ">= 0.4" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -7552,6 +7732,16 @@ "dev": true, "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -7637,6 +7827,30 @@ "node": ">= 0.10" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -8120,6 +8334,51 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -8482,6 +8741,69 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/tar-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -8599,6 +8921,18 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", diff --git a/package.json b/package.json index c88fe70..7515a19 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@stellar/stellar-sdk": "^14.6.1", "axios": "^1.6.2", + "better-sqlite3": "^12.8.0", "cors": "^2.8.6", "dotenv": "^17.3.1", "ethers": "^6.8.1", diff --git a/routes/payouts.js b/routes/payouts.js new file mode 100644 index 0000000..4a1e5fc --- /dev/null +++ b/routes/payouts.js @@ -0,0 +1,16 @@ +const express = require('express'); +const router = express.Router(); + +// GET /api/payouts?creator_address=... +router.get('/', (req, res) => { + const db = req.app.get('db'); + const { creator_address } = req.query; + try { + const payouts = db.getPayouts(creator_address); + res.json({ payouts }); + } catch (err) { + res.status(500).json({ error: 'Failed to fetch payouts' }); + } +}); + +module.exports = router; diff --git a/src/db/appDatabase.js b/src/db/appDatabase.js index cf16491..48a8ca9 100644 --- a/src/db/appDatabase.js +++ b/src/db/appDatabase.js @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); -const { DatabaseSync } = require('node:sqlite'); +const Database = require('better-sqlite3'); /** * SQLite-backed application database wrapper. @@ -13,30 +13,7 @@ class AppDatabase { constructor(filename) { this.filename = filename; this.ensureDirectory(); - this.db = new DatabaseSync(filename); - this.initializeSchema(); - this.ensureSubscriberCountColumn(); - } - - /** - * Ensure the database directory exists for file-backed databases. - * - * @returns {void} - */ - ensureDirectory() { - if (this.filename === ':memory:') { - return; - } - - fs.mkdirSync(path.dirname(this.filename), { recursive: true }); - } - - /** - * Initialize all application tables and indexes. - * - * @returns {void} - */ - initializeSchema() { + this.db = new Database(filename); this.db.exec(` PRAGMA foreign_keys = ON; @@ -88,18 +65,23 @@ class AppDatabase { PRIMARY KEY (creator_id, wallet_address) ); - CREATE TABLE IF NOT EXISTS subscriptions ( - creator_id TEXT NOT NULL REFERENCES creators(id), - wallet_address TEXT NOT NULL, - active INTEGER NOT NULL DEFAULT 1, - subscribed_at TEXT NOT NULL, - unsubscribed_at TEXT, - PRIMARY KEY (creator_id, wallet_address) - ); - CREATE INDEX IF NOT EXISTS idx_creator_audit_logs_creator_timestamp - ON creator_audit_logs (creator_id, timestamp DESC); + ON creator_audit_logs (creator_id, timestamp DESC); `); + + this.ensureSubscriberCountColumn(); + } + + /** + * Ensure the parent directory for the database file exists. + */ + ensureDirectory() { + if (this.filename !== ':memory:') { + const dir = path.dirname(this.filename); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + } } /** @@ -108,20 +90,12 @@ class AppDatabase { */ ensureSubscriberCountColumn() { try { - const info = this.db - .prepare("PRAGMA table_info(creators);") - .all(); - + const info = this.db.prepare('PRAGMA table_info(creators);').all(); const hasColumn = info.some((col) => col.name === 'subscriber_count'); - if (!hasColumn) { - // Add the column with a default of 0 - this.db.exec(`ALTER TABLE creators ADD COLUMN subscriber_count INTEGER DEFAULT 0`); + this.db.exec('ALTER TABLE creators ADD COLUMN subscriber_count INTEGER DEFAULT 0'); } } catch (error) { - // If anything goes wrong, log and continue โ€” schema migrations - // should be non-fatal for existing deployments in this simple codebase. - // eslint-disable-next-line no-console console.warn('ensureSubscriberCountColumn failed:', error.message); } } @@ -135,7 +109,6 @@ class AppDatabase { */ transaction(callback) { this.db.exec('BEGIN'); - try { const result = callback(); this.db.exec('COMMIT'); @@ -147,62 +120,42 @@ class AppDatabase { } /** - * Ensure a creator row exists. + * Ensure a creator row exists, inserting a stub if absent. * * @param {string} creatorId Creator identifier. - * @returns {void} */ ensureCreator(creatorId) { - const now = new Date().toISOString(); this.db .prepare( - ` - INSERT INTO creators (id, created_at) - VALUES (?, ?) - ON CONFLICT(id) DO NOTHING - `, + 'INSERT INTO creators (id, created_at) VALUES (?, ?) ON CONFLICT(id) DO NOTHING', ) - .run(creatorId, now); + .run(creatorId, new Date().toISOString()); } /** * Seed a video for tests or local setup. * * @param {{id: string, creatorId: string, title?: string, visibility: string}} video Video seed. - * @returns {void} */ seedVideo(video) { this.ensureCreator(video.creatorId); this.db .prepare( - ` - INSERT INTO videos (id, creator_id, title, visibility, updated_at) - VALUES (?, ?, ?, ?, ?) - `, + 'INSERT INTO videos (id, creator_id, title, visibility, updated_at) VALUES (?, ?, ?, ?, ?)', ) - .run( - video.id, - video.creatorId, - video.title || null, - video.visibility, - new Date().toISOString(), - ); + .run(video.id, video.creatorId, video.title || null, video.visibility, new Date().toISOString()); } /** * Seed a co-op split for tests or local setup. * - * @param {{id: string, creatorId: string, splits: object[]}} split Co-op split seed. - * @returns {void} + * @param {{id: string, creatorId: string, splits: object[]}} split Split seed. */ seedCoopSplit(split) { this.ensureCreator(split.creatorId); this.db .prepare( - ` - INSERT INTO coop_splits (id, creator_id, split_json, updated_at) - VALUES (?, ?, ?, ?) - `, + 'INSERT INTO coop_splits (id, creator_id, split_json, updated_at) VALUES (?, ?, ?, ?)', ) .run(split.id, split.creatorId, JSON.stringify(split.splits), new Date().toISOString()); } @@ -217,21 +170,9 @@ class AppDatabase { this.ensureCreator(settings.creatorId); this.db .prepare( - ` - INSERT INTO creator_settings (creator_id, flow_rate, currency, updated_at) - VALUES (?, ?, ?, ?) - ON CONFLICT(creator_id) DO UPDATE SET - flow_rate = excluded.flow_rate, - currency = excluded.currency, - updated_at = excluded.updated_at - `, + 'INSERT INTO creator_settings (creator_id, flow_rate, currency, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT(creator_id) DO UPDATE SET flow_rate = excluded.flow_rate, currency = excluded.currency, updated_at = excluded.updated_at', ) - .run( - settings.creatorId, - settings.flowRate, - settings.currency || null, - new Date().toISOString(), - ); + .run(settings.creatorId, settings.flowRate, settings.currency || null, new Date().toISOString()); } /** @@ -243,14 +184,9 @@ class AppDatabase { getCreatorSettings(creatorId) { const row = this.db .prepare( - ` - SELECT creator_id AS creatorId, flow_rate AS flowRate, currency, updated_at AS updatedAt - FROM creator_settings - WHERE creator_id = ? - `, + 'SELECT creator_id AS creatorId, flow_rate AS flowRate, currency, updated_at AS updatedAt FROM creator_settings WHERE creator_id = ?', ) .get(creatorId); - return row || null; } @@ -264,17 +200,9 @@ class AppDatabase { this.ensureCreator(settings.creatorId); this.db .prepare( - ` - INSERT INTO creator_settings (creator_id, flow_rate, currency, updated_at) - VALUES (?, ?, ?, ?) - ON CONFLICT(creator_id) DO UPDATE SET - flow_rate = excluded.flow_rate, - currency = excluded.currency, - updated_at = excluded.updated_at - `, + 'INSERT INTO creator_settings (creator_id, flow_rate, currency, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT(creator_id) DO UPDATE SET flow_rate = excluded.flow_rate, currency = excluded.currency, updated_at = excluded.updated_at', ) .run(settings.creatorId, settings.flowRate, settings.currency, settings.updatedAt); - return this.getCreatorSettings(settings.creatorId); } @@ -287,14 +215,9 @@ class AppDatabase { getVideoById(videoId) { const row = this.db .prepare( - ` - SELECT id, creator_id AS creatorId, title, visibility, updated_at AS updatedAt - FROM videos - WHERE id = ? - `, + 'SELECT id, creator_id AS creatorId, title, visibility, updated_at AS updatedAt FROM videos WHERE id = ?', ) .get(videoId); - return row || null; } @@ -306,15 +229,8 @@ class AppDatabase { */ updateVideoVisibility(input) { this.db - .prepare( - ` - UPDATE videos - SET visibility = ?, updated_at = ? - WHERE id = ? - `, - ) + .prepare('UPDATE videos SET visibility = ?, updated_at = ? WHERE id = ?') .run(input.visibility, input.updatedAt, input.videoId); - return this.getVideoById(input.videoId); } @@ -327,18 +243,12 @@ class AppDatabase { getCoopSplitById(splitId) { const row = this.db .prepare( - ` - SELECT id, creator_id AS creatorId, split_json AS splitJson, updated_at AS updatedAt - FROM coop_splits - WHERE id = ? - `, + 'SELECT id, creator_id AS creatorId, split_json AS splitJson, updated_at AS updatedAt FROM coop_splits WHERE id = ?', ) .get(splitId); - if (!row) { return null; } - return { ...row, splits: JSON.parse(row.splitJson), @@ -353,15 +263,8 @@ class AppDatabase { */ updateCoopSplit(input) { this.db - .prepare( - ` - UPDATE coop_splits - SET split_json = ?, updated_at = ? - WHERE id = ? - `, - ) + .prepare('UPDATE coop_splits SET split_json = ?, updated_at = ? WHERE id = ?') .run(JSON.stringify(input.splits), input.updatedAt, input.splitId); - return this.getCoopSplitById(input.splitId); } @@ -376,20 +279,7 @@ class AppDatabase { this.ensureCreator(entry.creatorId); this.db .prepare( - ` - INSERT INTO creator_audit_logs ( - id, - creator_id, - action_type, - entity_type, - entity_id, - timestamp, - ip_address, - metadata_json, - created_at - ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - `, + 'INSERT INTO creator_audit_logs (id, creator_id, action_type, entity_type, entity_id, timestamp, ip_address, metadata_json, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', ) .run( id, @@ -405,20 +295,7 @@ class AppDatabase { return this.db .prepare( - ` - SELECT - id, - creator_id AS creatorId, - action_type AS actionType, - entity_type AS entityType, - entity_id AS entityId, - timestamp, - ip_address AS ipAddress, - metadata_json AS metadataJson, - created_at AS createdAt - FROM creator_audit_logs - WHERE id = ? - `, + 'SELECT id, creator_id AS creatorId, action_type AS actionType, entity_type AS entityType, entity_id AS entityId, timestamp, ip_address AS ipAddress, metadata_json AS metadataJson, created_at AS createdAt FROM creator_audit_logs WHERE id = ?', ) .get(id); } @@ -432,21 +309,7 @@ class AppDatabase { listAuditLogsByCreatorId(creatorId) { return this.db .prepare( - ` - SELECT - id, - creator_id AS creatorId, - action_type AS actionType, - entity_type AS entityType, - entity_id AS entityId, - timestamp, - ip_address AS ipAddress, - metadata_json AS metadataJson, - created_at AS createdAt - FROM creator_audit_logs - WHERE creator_id = ? - ORDER BY timestamp DESC, id DESC - `, + 'SELECT id, creator_id AS creatorId, action_type AS actionType, entity_type AS entityType, entity_id AS entityId, timestamp, ip_address AS ipAddress, metadata_json AS metadataJson, created_at AS createdAt FROM creator_audit_logs WHERE creator_id = ? ORDER BY timestamp DESC, id DESC', ) .all(creatorId); } @@ -461,9 +324,8 @@ class AppDatabase { getCreatorSubscriberCount(creatorId) { this.ensureCreator(creatorId); const row = this.db - .prepare(`SELECT subscriber_count AS subscriberCount FROM creators WHERE id = ?`) + .prepare('SELECT subscriber_count AS subscriberCount FROM creators WHERE id = ?') .get(creatorId); - return (row && Number(row.subscriberCount)) || 0; } @@ -478,9 +340,10 @@ class AppDatabase { return this.transaction(() => { this.ensureCreator(creatorId); this.db - .prepare(`UPDATE creators SET subscriber_count = COALESCE(subscriber_count, 0) + 1 WHERE id = ?`) + .prepare( + 'UPDATE creators SET subscriber_count = COALESCE(subscriber_count, 0) + 1 WHERE id = ?', + ) .run(creatorId); - return this.getCreatorSubscriberCount(creatorId); }); } @@ -497,10 +360,9 @@ class AppDatabase { this.ensureCreator(creatorId); this.db .prepare( - `UPDATE creators SET subscriber_count = MAX(COALESCE(subscriber_count, 0) - 1, 0) WHERE id = ?`, + 'UPDATE creators SET subscriber_count = MAX(COALESCE(subscriber_count, 0) - 1, 0) WHERE id = ?', ) .run(creatorId); - return this.getCreatorSubscriberCount(creatorId); }); } @@ -517,15 +379,15 @@ class AppDatabase { this.ensureCreator(creatorId); const safe = Math.max(0, Math.floor(Number(count) || 0)); this.db - .prepare(`UPDATE creators SET subscriber_count = ? WHERE id = ?`) + .prepare('UPDATE creators SET subscriber_count = ? WHERE id = ?') .run(safe, creatorId); - return this.getCreatorSubscriberCount(creatorId); }); } /** * Get a subscription row for a creator and wallet. + * * @param {string} creatorId * @param {string} walletAddress * @returns {object|null} @@ -533,15 +395,16 @@ class AppDatabase { getSubscription(creatorId, walletAddress) { const row = this.db .prepare( - `SELECT creator_id AS creatorId, wallet_address AS walletAddress, active, subscribed_at AS subscribedAt, unsubscribed_at AS unsubscribedAt FROM subscriptions WHERE creator_id = ? AND wallet_address = ?`, + 'SELECT creator_id AS creatorId, wallet_address AS walletAddress, active, subscribed_at AS subscribedAt, unsubscribed_at AS unsubscribedAt FROM subscriptions WHERE creator_id = ? AND wallet_address = ?', ) .get(creatorId, walletAddress); - return row || null; } /** - * Create or activate a subscription for a wallet. Returns { changed: boolean, count: number } + * Create or activate a subscription for a wallet. + * Returns { changed: boolean, count: number }. + * * @param {string} creatorId * @param {string} walletAddress */ @@ -552,33 +415,37 @@ class AppDatabase { const now = new Date().toISOString(); if (existing && existing.active === 1) { - // already active return { changed: false, count: this.getCreatorSubscriberCount(creatorId) }; } if (existing) { - // reactivate this.db - .prepare(`UPDATE subscriptions SET active = 1, subscribed_at = ?, unsubscribed_at = NULL WHERE creator_id = ? AND wallet_address = ?`) + .prepare( + 'UPDATE subscriptions SET active = 1, subscribed_at = ?, unsubscribed_at = NULL WHERE creator_id = ? AND wallet_address = ?', + ) .run(now, creatorId, walletAddress); } else { this.db - .prepare(`INSERT INTO subscriptions (creator_id, wallet_address, active, subscribed_at) VALUES (?, ?, 1, ?)`) + .prepare( + 'INSERT INTO subscriptions (creator_id, wallet_address, active, subscribed_at) VALUES (?, ?, 1, ?)', + ) .run(creatorId, walletAddress, now); } - // Increment cached count this.db - .prepare(`UPDATE creators SET subscriber_count = COALESCE(subscriber_count, 0) + 1 WHERE id = ?`) + .prepare( + 'UPDATE creators SET subscriber_count = COALESCE(subscriber_count, 0) + 1 WHERE id = ?', + ) .run(creatorId); - const newCount = this.getCreatorSubscriberCount(creatorId); - return { changed: true, count: newCount }; + return { changed: true, count: this.getCreatorSubscriberCount(creatorId) }; }); } /** - * Deactivate a subscription if it is currently active. Returns { changed: boolean, count: number } + * Deactivate a subscription if it is currently active. + * Returns { changed: boolean, count: number }. + * * @param {string} creatorId * @param {string} walletAddress */ @@ -593,33 +460,35 @@ class AppDatabase { } this.db - .prepare(`UPDATE subscriptions SET active = 0, unsubscribed_at = ? WHERE creator_id = ? AND wallet_address = ?`) + .prepare( + 'UPDATE subscriptions SET active = 0, unsubscribed_at = ? WHERE creator_id = ? AND wallet_address = ?', + ) .run(now, creatorId, walletAddress); - // Decrement cached count, clamp at 0 this.db - .prepare(`UPDATE creators SET subscriber_count = MAX(COALESCE(subscriber_count, 0) - 1, 0) WHERE id = ?`) + .prepare( + 'UPDATE creators SET subscriber_count = MAX(COALESCE(subscriber_count, 0) - 1, 0) WHERE id = ?', + ) .run(creatorId); - const newCount = this.getCreatorSubscriberCount(creatorId); - return { changed: true, count: newCount }; + return { changed: true, count: this.getCreatorSubscriberCount(creatorId) }; }); } /** * Count active subscriptions for a creator (derived from subscriptions table). + * * @param {string} creatorId * @returns {number} */ countActiveSubscriptions(creatorId) { const row = this.db - .prepare(`SELECT COUNT(1) AS ct FROM subscriptions WHERE creator_id = ? AND active = 1`) + .prepare('SELECT COUNT(1) AS ct FROM subscriptions WHERE creator_id = ? AND active = 1') .get(creatorId); - return (row && Number(row.ct)) || 0; } } module.exports = { AppDatabase, -}; +}; \ No newline at end of file From 5c3ff4373890788236dcfc6b370701eaebcbe231 Mon Sep 17 00:00:00 2001 From: Skinny001 Date: Wed, 25 Mar 2026 13:42:48 +0100 Subject: [PATCH 2/4] Remove node_modules from git tracking --- .env.example | 25 ++ .github/workflows/test.yml | 10 +- README.md | 53 +++- docs/VIDEO_TRANSCODING_API.md | 376 ++++++++++++++++++++++ index.js | 12 +- package.json | 8 + routes/video.js | 397 ++++++++++++++++++++++++ src/config.js | 24 ++ src/db/appDatabase.js | 20 +- src/services/videoProcessingWorker.js | 212 +++++++++++++ src/services/videoTranscodingService.js | 199 ++++++++++++ test-video-transcoding.js | 108 +++++++ 12 files changed, 1422 insertions(+), 22 deletions(-) create mode 100644 docs/VIDEO_TRANSCODING_API.md create mode 100644 routes/video.js create mode 100644 src/services/videoProcessingWorker.js create mode 100644 src/services/videoTranscodingService.js create mode 100644 test-video-transcoding.js diff --git a/.env.example b/.env.example index 65b609d..efd3fee 100644 --- a/.env.example +++ b/.env.example @@ -55,3 +55,28 @@ INFURA_API_KEY=your-infura-api-key # INFLUXDB_TOKEN=your-influxdb-token # INFLUXDB_ORG=your-org # INFLUXDB_BUCKET=substream-analytics + +# Video Transcoding Configuration +FFMPEG_PATH=/usr/bin/ffmpeg +TRANSCODING_OUTPUT_DIR=./transcoded +MAX_CONCURRENT_TRANSCODINGS=3 + +# Redis Configuration (for job queue) +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= +REDIS_DB=0 + +# S3 Configuration (for video storage) +S3_BUCKET=your-s3-bucket +S3_REGION=us-east-1 +S3_ACCESS_KEY_ID=your-access-key +S3_SECRET_ACCESS_KEY=your-secret-key + +# IPFS Configuration +IPFS_HOST=localhost +IPFS_PORT=5001 +IPFS_PROTOCOL=http + +# CDN Configuration for video streaming +CDN_BASE_URL=https://cdn.example.com/videos diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index de97405..2a708bf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,10 +10,14 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '20.11.0' + - name: Install FFmpeg + run: | + sudo apt-get update + sudo apt-get install -y ffmpeg - run: npm ci - run: npm test diff --git a/README.md b/README.md index 80659fb..e9edc01 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@ A comprehensive backend API for the SubStream Protocol, supporting wallet-based ## Features +### ๐ŸŽฅ Video Transcoding & Streaming +- **Multi-resolution transcoding**: Automatic conversion to 360p, 720p, and 1080p +- **HLS streaming**: Segmented video for smooth adaptive bitrate streaming +- **Adaptive quality**: Automatic quality selection based on connection speed +- **Background processing**: Queue-based transcoding with Redis +- **Storage flexibility**: Support for S3 and IPFS storage +- **Pay-per-second integration**: Seamless integration with subscription system + ### ๐Ÿ” Authentication (SIWE) - Wallet-based authentication using Sign In With Ethereum - JWT token generation and validation @@ -32,33 +40,64 @@ A comprehensive backend API for the SubStream Protocol, supporting wallet-based ## Quick Start ### Prerequisites -- Node.js 16+ +- Node.js 20.11.0+ - npm or yarn +- FFmpeg (for video transcoding) +- Redis (for job queue) ### Installation 1. Clone the repository: ```bash -git clone https://github.com/jobbykings/SubStream-Protocol-Backend.git +git clone https://github.com/lifewithbigdamz/SubStream-Protocol-Backend.git cd SubStream-Protocol-Backend ``` -2. Install dependencies: +2. Install FFmpeg: +```bash +# Ubuntu/Debian +sudo apt-get update && sudo apt-get install -y ffmpeg + +# macOS +brew install ffmpeg + +# Windows +# Download from https://ffmpeg.org/download.html +``` + +3. Install and start Redis: +```bash +# Ubuntu/Debian +sudo apt-get install redis-server +sudo systemctl start redis + +# macOS +brew install redis +brew services start redis + +# Windows +# Download from https://redis.io/download +``` + +4. Install dependencies: ```bash npm install ``` -3. Copy environment variables: +5. Copy environment variables: ```bash cp .env.example .env ``` -4. Configure your environment variables in `.env`: +6. Configure your environment variables in `.env`: - Set your JWT secret - Add IPFS service API keys -- Configure database connections (optional for development) +- Configure Redis connection +- Set up S3 credentials (optional) +- Configure FFmpeg path +- Set up CDN base URL -5. Start the server: +7. Start the server: ```bash # Development npm run dev diff --git a/docs/VIDEO_TRANSCODING_API.md b/docs/VIDEO_TRANSCODING_API.md new file mode 100644 index 0000000..60a3205 --- /dev/null +++ b/docs/VIDEO_TRANSCODING_API.md @@ -0,0 +1,376 @@ +# Video Transcoding API Documentation + +## Overview + +The SubStream Protocol backend now includes a comprehensive video transcoding and streaming system that converts uploaded videos into HLS (HTTP Live Streaming) format with multiple resolutions for adaptive bitrate streaming. + +## Features + +- **Multi-resolution transcoding**: 360p, 720p, and 1080p +- **HLS streaming**: Segmented video for smooth playback +- **Adaptive bitrate**: Automatic quality selection based on connection speed +- **Storage options**: IPFS and S3-compatible storage +- **Queue management**: Background processing with Redis +- **Pay-per-second streaming**: Integration with existing subscription system + +## Configuration + +### Environment Variables + +```bash +# Video Transcoding Configuration +FFMPEG_PATH=/usr/bin/ffmpeg +TRANSCODING_OUTPUT_DIR=./transcoded +MAX_CONCURRENT_TRANSCODINGS=3 + +# Redis Configuration (for job queue) +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= +REDIS_DB=0 + +# S3 Configuration (for video storage) +S3_BUCKET=your-s3-bucket +S3_REGION=us-east-1 +S3_ACCESS_KEY_ID=your-access-key +S3_SECRET_ACCESS_KEY=your-secret-key + +# IPFS Configuration +IPFS_HOST=localhost +IPFS_PORT=5001 +IPFS_PROTOCOL=http + +# CDN Configuration for video streaming +CDN_BASE_URL=https://cdn.example.com/videos +``` + +## API Endpoints + +### Video Upload + +**POST** `/api/videos/upload` + +Upload a video file for transcoding and streaming. + +**Request:** +- Content-Type: `multipart/form-data` +- Body: + - `video`: Video file (mp4, avi, mov, wmv, flv) + - `title`: Video title (required) + - `description`: Video description (optional) + - `creatorId`: Creator ID (required) + - `visibility`: Video visibility (optional, default: 'private') + +**Response:** +```json +{ + "success": true, + "data": { + "videoId": "uuid", + "title": "My Video", + "status": "uploaded", + "jobId": "job-uuid", + "message": "Video uploaded successfully. Transcoding started." + } +} +``` + +### Get Video Playlist + +**GET** `/api/videos/:videoId/playlist` + +Get the HLS master playlist for streaming. + +**Query Parameters:** +- `quality`: Optional quality override (360p, 720p, 1080p) +- `walletAddress`: Required for private videos + +**Response:** +- Content-Type: `application/vnd.apple.mpegurl` +- HLS playlist content + +### Get Video Segment + +**GET** `/api/videos/:videoId/segment/:segmentName` + +Get a specific video segment for streaming. + +**Query Parameters:** +- `walletAddress`: Required for private videos + +**Response:** +- Content-Type: `video/mp2t` +- Video segment data + +### Get Video Status + +**GET** `/api/videos/:videoId/status` + +Get the processing status of a video. + +**Response:** +```json +{ + "success": true, + "data": { + "videoId": "uuid", + "title": "My Video", + "status": "completed", + "message": "Transcoding completed successfully", + "processingStatus": { + "status": "completed", + "message": "Transcoding completed successfully", + "timestamp": "2024-01-01T00:00:00.000Z" + }, + "createdAt": "2024-01-01T00:00:00.000Z", + "updatedAt": "2024-01-01T00:00:00.000Z" + } +} +``` + +### Analyze Connection Quality + +**POST** `/api/videos/:videoId/analyze-connection` + +Analyze user's connection and recommend optimal video quality. + +**Request Body:** +```json +{ + "bandwidth": 5.0, + "latency": 50, + "packetLoss": 0.01 +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "videoId": "uuid", + "jobId": "job-uuid", + "message": "Connection analysis started" + } +} +``` + +### Get Recommended Quality + +**GET** `/api/videos/:videoId/recommended-quality` + +Get the recommended video quality based on connection analysis. + +**Response:** +```json +{ + "success": true, + "data": { + "videoId": "uuid", + "recommendedQuality": "720p" + } +} +``` + +### Get Queue Statistics + +**GET** `/api/videos/queue/stats` + +Get statistics about the video processing queue. + +**Response:** +```json +{ + "success": true, + "data": { + "waiting": 2, + "active": 1, + "completed": 15, + "failed": 0 + } +} +``` + +## Video Processing Workflow + +1. **Upload**: User uploads video via `/api/videos/upload` +2. **Queue**: Video is added to Redis queue for processing +3. **Transcoding**: Background worker processes video: + - Extracts video metadata + - Creates 360p, 720p, and 1080p versions + - Generates HLS segments (10-second chunks) + - Creates master playlist +4. **Storage**: Transcoded files are uploaded to: + - S3 bucket (if configured) + - IPFS (if configured) +5. **Cleanup**: Local temporary files are removed +6. **Streaming**: Videos are available via HLS endpoints + +## Adaptive Bitrate Logic + +The system analyzes connection metrics to recommend optimal quality: + +- **360p**: Bandwidth < 1 Mbps OR packet loss > 5% +- **720p**: Bandwidth < 3 Mbps OR latency > 200ms +- **1080p**: Bandwidth โ‰ฅ 3 Mbps AND latency โ‰ค 200ms AND packet loss โ‰ค 5% + +## Storage Integration + +### S3 Storage +- Automatic upload of all transcoded files +- Organized structure: `videos/{videoId}/{filename}` +- Public or private access based on configuration + +### IPFS Storage +- Content-addressed storage +- Automatic pinning to IPFS network +- Returns IPFS hashes for each file + +## Error Handling + +### Common Error Responses + +**400 Bad Request:** +```json +{ + "success": false, + "error": "No video file provided" +} +``` + +**403 Forbidden:** +```json +{ + "success": false, + "error": "Access denied. Active subscription required." +} +``` + +**404 Not Found:** +```json +{ + "success": false, + "error": "Video not found" +} +``` + +**500 Internal Server Error:** +```json +{ + "success": false, + "error": "Failed to upload video" +} +``` + +## Integration with Pay-per-Second Streaming + +The video transcoding system integrates seamlessly with the existing pay-per-second streaming: + +1. **Authentication**: Video access requires valid subscription verification +2. **Token Generation**: CDN tokens are generated for authorized access +3. **Quality Control**: Adaptive bitrate ensures smooth streaming without buffering +4. **Usage Tracking**: Each segment request can be tracked for billing + +## Performance Considerations + +- **Concurrent Processing**: Configurable limit on simultaneous transcoding jobs +- **Queue Management**: Redis-based queue with retry logic +- **Storage Optimization**: Automatic cleanup of temporary files +- **CDN Integration**: Support for CDN-based delivery + +## Security + +- **File Validation**: Only allowed video formats are accepted +- **Size Limits**: Configurable maximum file size (default: 2GB) +- **Access Control**: Private videos require subscription verification +- **Token Security**: JWT-based access tokens for CDN + +## Monitoring + +- **Job Status**: Real-time status tracking of transcoding jobs +- **Queue Metrics**: Statistics on processing queue health +- **Error Logging**: Comprehensive error tracking and logging +- **Performance Metrics**: Transcoding time and success rates + +## Example Usage + +### Upload and Stream a Video + +```javascript +// 1. Upload video +const formData = new FormData(); +formData.append('video', videoFile); +formData.append('title', 'My Awesome Video'); +formData.append('creatorId', 'creator-uuid'); +formData.append('visibility', 'public'); + +const uploadResponse = await fetch('/api/videos/upload', { + method: 'POST', + body: formData +}); + +const { videoId } = await uploadResponse.json(); + +// 2. Get playlist +const playlistResponse = await fetch(`/api/videos/${videoId}/playlist`); +const playlist = await playlistResponse.text(); + +// 3. Stream video (using HLS.js or similar) +const hls = new Hls(); +hls.loadSource(`/api/videos/${videoId}/playlist`); +hls.attachMedia(videoElement); +``` + +### Adaptive Quality Selection + +```javascript +// Analyze connection +const connectionData = { + bandwidth: 4.5, // Mbps + latency: 120, // ms + packetLoss: 0.02 // 2% +}; + +await fetch(`/api/videos/${videoId}/analyze-connection`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(connectionData) +}); + +// Get recommended quality +const qualityResponse = await fetch(`/api/videos/${videoId}/recommended-quality`); +const { recommendedQuality } = await qualityResponse.json(); + +// Get specific quality playlist +const qualityPlaylist = await fetch(`/api/videos/${videoId}/playlist?quality=${recommendedQuality}`); +``` + +## Troubleshooting + +### Common Issues + +1. **FFmpeg not found**: Ensure FFmpeg is installed and accessible +2. **Redis connection failed**: Check Redis configuration and connectivity +3. **S3 upload failed**: Verify S3 credentials and permissions +4. **Video processing stuck**: Check queue statistics and worker logs + +### Debug Commands + +```bash +# Check queue status +curl http://localhost:3000/api/videos/queue/stats + +# Check video status +curl http://localhost:3000/api/videos/{videoId}/status + +# Test playlist access +curl http://localhost:3000/api/videos/{videoId}/playlist +``` + +## Future Enhancements + +- **Real-time transcoding**: Live stream processing +- **AI-based quality optimization**: Machine learning for quality selection +- **Advanced codecs**: Support for AV1 and VP9 +- **Multi-language audio tracks**: Support for multiple audio streams +- **Thumbnail generation**: Automatic video thumbnail creation diff --git a/index.js b/index.js index eb4634f..1235507 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,6 @@ const { CreatorActionService } = require('./src/services/creatorActionService'); const { CreatorAuditLogService } = require('./src/services/creatorAuditLogService'); const { CreatorAuthService } = require('./src/services/creatorAuthService'); const { SorobanSubscriptionVerifier } = require('./src/services/sorobanSubscriptionVerifier'); -const { SubscriptionService } = require('./src/services/subscriptionService'); const { buildAuditLogCsv } = require('./src/utils/export/auditLogCsv'); const { buildAuditLogPdf } = require('./src/utils/export/auditLogPdf'); const { getRequestIp } = require('./src/utils/requestIp'); @@ -33,18 +32,9 @@ function createApp(dependencies = {}) { const subscriptionVerifier = dependencies.subscriptionVerifier || new SorobanSubscriptionVerifier(config); const tokenService = dependencies.tokenService || new CdnTokenService(config); - const subscriptionService = - dependencies.subscriptionService || new SubscriptionService({ database, auditLogService }); - - // expose the service on the express app so external routers can access it - app.set('subscriptionService', subscriptionService); app.use(cors()); app.use(express.json()); - // Subscription events webhook - app.use('/api/subscription', require('./routes/subscription')); - // Payouts API - app.use('/api/payouts', require('./routes/payouts')); app.get('/', (req, res) => { res.json({ @@ -261,6 +251,8 @@ function createApp(dependencies = {}) { return res.status(200).send(pdf); }); + app.use('/api/videos', createVideoRoutes(config, database, videoWorker)); + app.use((req, res) => res.status(404).json({ success: false, error: 'Not found' })); return app; diff --git a/package.json b/package.json index 7515a19..e67b49c 100644 --- a/package.json +++ b/package.json @@ -10,19 +10,27 @@ }, "dependencies": { "@stellar/stellar-sdk": "^14.6.1", + "aws-sdk": "^2.1500.0", "axios": "^1.6.2", "better-sqlite3": "^12.8.0", + "bull": "^4.12.2", "cors": "^2.8.6", "dotenv": "^17.3.1", "ethers": "^6.8.1", "express": "^5.2.1", + "fluent-ffmpeg": "^2.1.2", "form-data": "^4.0.0", "ipfs-http-client": "^60.0.1", "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", + "redis": "^4.6.10", "stellar-sdk": "^13.3.0", + "uuid": "^9.0.1", "web3.storage": "^4.5.4" }, + "engines": { + "node": ">=20.11.0" + }, "devDependencies": { "jest": "^29.7.0", "nodemon": "^3.0.2", diff --git a/routes/video.js b/routes/video.js new file mode 100644 index 0000000..e1e84c3 --- /dev/null +++ b/routes/video.js @@ -0,0 +1,397 @@ +const express = require('express'); +const multer = require('multer'); +const path = require('path'); +const fs = require('fs').promises; +const { v4: uuidv4 } = require('uuid'); + +const router = express.Router(); + +const storage = multer.diskStorage({ + destination: async (req, file, cb) => { + const uploadDir = path.join(process.cwd(), 'uploads'); + await fs.mkdir(uploadDir, { recursive: true }); + cb(null, uploadDir); + }, + filename: (req, file, cb) => { + const uniqueSuffix = uuidv4(); + cb(null, `${uniqueSuffix}-${file.originalname}`); + } +}); + +const upload = multer({ + storage, + limits: { + fileSize: 2 * 1024 * 1024 * 1024 // 2GB limit + }, + fileFilter: (req, file, cb) => { + const allowedTypes = ['video/mp4', 'video/avi', 'video/mov', 'video/wmv', 'video/flv']; + if (allowedTypes.includes(file.mimetype)) { + cb(null, true); + } else { + cb(new Error('Invalid file type. Only video files are allowed.'), false); + } + } +}); + +function createVideoRoutes(config, database, videoWorker) { + router.post('/upload', upload.single('video'), async (req, res) => { + try { + if (!req.file) { + return res.status(400).json({ + success: false, + error: 'No video file provided' + }); + } + + const { title, description, creatorId, visibility = 'private' } = req.body; + + if (!title || !creatorId) { + return res.status(400).json({ + success: false, + error: 'Title and creator ID are required' + }); + } + + const videoId = uuidv4(); + + await database.run( + `INSERT INTO videos + (id, title, description, creator_id, original_filename, file_path, file_size, status, visibility, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, 'uploaded', ?, CURRENT_TIMESTAMP)`, + [ + videoId, + title, + description || null, + creatorId, + req.file.originalname, + req.file.path, + req.file.size, + visibility + ] + ); + + const transcodingJob = await videoWorker.addTranscodingJob( + videoId, + req.file.path, + { + priority: visibility === 'public' ? 'high' : 'normal' + } + ); + + res.status(201).json({ + success: true, + data: { + videoId, + title, + status: 'uploaded', + jobId: transcodingJob.id, + message: 'Video uploaded successfully. Transcoding started.' + } + }); + + } catch (error) { + console.error('Upload error:', error); + res.status(500).json({ + success: false, + error: 'Failed to upload video' + }); + } + }); + + router.get('/:videoId/playlist', async (req, res) => { + try { + const { videoId } = req.params; + const { quality } = req.query; + + const video = await database.get( + 'SELECT * FROM videos WHERE id = ?', + [videoId] + ); + + if (!video) { + return res.status(404).json({ + success: false, + error: 'Video not found' + }); + } + + if (video.visibility === 'private') { + const authResult = await req.subscriptionVerifier?.verifySubscription({ + walletAddress: req.query.walletAddress, + creatorAddress: video.creator_id, + contentId: videoId, + segmentPath: 'master.m3u8' + }); + + if (!authResult?.active) { + return res.status(403).json({ + success: false, + error: 'Access denied. Active subscription required.' + }); + } + } + + const transcodingResult = await database.get( + 'SELECT * FROM transcoding_results WHERE video_id = ?', + [videoId] + ); + + if (!transcodingResult) { + return res.status(404).json({ + success: false, + error: 'Video not yet processed' + }); + } + + const masterPlaylist = JSON.parse(transcodingResult.master_playlist); + + if (quality) { + const qualityPlaylist = await getQualityPlaylist(videoId, quality, database); + if (qualityPlaylist) { + res.setHeader('Content-Type', 'application/vnd.apple.mpegurl'); + return res.send(qualityPlaylist); + } + } + + res.setHeader('Content-Type', 'application/vnd.apple.mpegurl'); + res.send(masterPlaylist.content); + + } catch (error) { + console.error('Playlist error:', error); + res.status(500).json({ + success: false, + error: 'Failed to get playlist' + }); + } + }); + + router.get('/:videoId/segment/:segmentName', async (req, res) => { + try { + const { videoId, segmentName } = req.params; + + const video = await database.get( + 'SELECT * FROM videos WHERE id = ?', + [videoId] + ); + + if (!video) { + return res.status(404).json({ + success: false, + error: 'Video not found' + }); + } + + if (video.visibility === 'private') { + const authResult = await req.subscriptionVerifier?.verifySubscription({ + walletAddress: req.query.walletAddress, + creatorAddress: video.creator_id, + contentId: videoId, + segmentPath: segmentName + }); + + if (!authResult?.active) { + return res.status(403).json({ + success: false, + error: 'Access denied. Active subscription required.' + }); + } + } + + const segmentUrl = getSegmentUrl(videoId, segmentName, req.config); + + if (segmentUrl.startsWith('http')) { + const response = await fetch(segmentUrl); + if (!response.ok) { + return res.status(404).json({ + success: false, + error: 'Segment not found' + }); + } + + res.setHeader('Content-Type', 'video/mp2t'); + return res.send(await response.arrayBuffer()); + } + + res.status(404).json({ + success: false, + error: 'Segment not found' + }); + + } catch (error) { + console.error('Segment error:', error); + res.status(500).json({ + success: false, + error: 'Failed to get segment' + }); + } + }); + + router.get('/:videoId/status', async (req, res) => { + try { + const { videoId } = req.params; + + const video = await database.get( + 'SELECT * FROM videos WHERE id = ?', + [videoId] + ); + + if (!video) { + return res.status(404).json({ + success: false, + error: 'Video not found' + }); + } + + const processingStatus = await videoWorker.getVideoStatus(videoId); + + res.json({ + success: true, + data: { + videoId: video.id, + title: video.title, + status: video.status, + message: video.message, + processingStatus, + createdAt: video.created_at, + updatedAt: video.updated_at + } + }); + + } catch (error) { + console.error('Status error:', error); + res.status(500).json({ + success: false, + error: 'Failed to get video status' + }); + } + }); + + router.post('/:videoId/analyze-connection', async (req, res) => { + try { + const { videoId } = req.params; + const { bandwidth, latency, packetLoss } = req.body; + + if (!bandwidth || !latency || packetLoss === undefined) { + return res.status(400).json({ + success: false, + error: 'Bandwidth, latency, and packet loss are required' + }); + } + + const connectionMetrics = { + bandwidth: parseFloat(bandwidth), + latency: parseInt(latency), + packetLoss: parseFloat(packetLoss) + }; + + const job = await videoWorker.addAdaptiveBitrateJob(videoId, connectionMetrics); + + res.json({ + success: true, + data: { + videoId, + jobId: job.id, + message: 'Connection analysis started' + } + }); + + } catch (error) { + console.error('Connection analysis error:', error); + res.status(500).json({ + success: false, + error: 'Failed to analyze connection' + }); + } + }); + + router.get('/:videoId/recommended-quality', async (req, res) => { + try { + const { videoId } = req.params; + + const recommendedQuality = await videoWorker.getRecommendedQuality(videoId); + + res.json({ + success: true, + data: { + videoId, + recommendedQuality + } + }); + + } catch (error) { + console.error('Quality recommendation error:', error); + res.status(500).json({ + success: false, + error: 'Failed to get recommended quality' + }); + } + }); + + router.get('/queue/stats', async (req, res) => { + try { + const stats = await videoWorker.getQueueStats(); + + res.json({ + success: true, + data: stats + }); + + } catch (error) { + console.error('Queue stats error:', error); + res.status(500).json({ + success: false, + error: 'Failed to get queue statistics' + }); + } + }); + + return router; +} + +async function getQualityPlaylist(videoId, quality, database) { + try { + const transcodingResult = await database.get( + 'SELECT * FROM transcoding_results WHERE video_id = ?', + [videoId] + ); + + if (!transcodingResult) return null; + + const resolutions = JSON.parse(transcodingResult.resolutions); + const targetResolution = resolutions.find(r => r.resolution === quality); + + if (!targetResolution) return null; + + return `#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-TARGETDURATION:10 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD + +#EXTINF:10.0, +${targetResolution.segments.replace('%03d', '001')} +#EXTINF:10.0, +${targetResolution.segments.replace('%03d', '002')} +#EXTINF:10.0, +${targetResolution.segments.replace('%03d', '003')} +#EXT-X-ENDLIST`; + + } catch (error) { + console.error('Quality playlist error:', error); + return null; + } +} + +function getSegmentUrl(videoId, segmentName, config) { + if (config.cdn?.baseUrl) { + return `${config.cdn.baseUrl}/${videoId}/${segmentName}`; + } + + if (config.s3?.bucket && config.s3?.region) { + return `https://${config.s3.bucket}.s3.${config.s3.region}.amazonaws.com/videos/${videoId}/${segmentName}`; + } + + return `/videos/${videoId}/${segmentName}`; +} + +module.exports = createVideoRoutes; diff --git a/src/config.js b/src/config.js index 8a766ff..5901eb5 100644 --- a/src/config.js +++ b/src/config.js @@ -39,6 +39,30 @@ function loadConfig(env = process.env) { argumentMapping: env.SOROBAN_SUBSCRIPTION_ARGUMENTS || 'address:walletAddress,address:creatorAddress', }, + transcoding: { + ffmpegPath: env.FFMPEG_PATH || 'ffmpeg', + outputDir: env.TRANSCODING_OUTPUT_DIR || './transcoded', + maxConcurrent: Number(env.MAX_CONCURRENT_TRANSCODINGS || 3), + }, + redis: { + host: env.REDIS_HOST || 'localhost', + port: Number(env.REDIS_PORT || 6379), + password: env.REDIS_PASSWORD || '', + db: Number(env.REDIS_DB || 0), + }, + s3: env.S3_BUCKET ? { + bucket: env.S3_BUCKET, + region: env.S3_REGION || 'us-east-1', + credentials: { + accessKeyId: env.S3_ACCESS_KEY_ID, + secretAccessKey: env.S3_SECRET_ACCESS_KEY, + }, + } : null, + ipfs: env.IPFS_HOST ? { + host: env.IPFS_HOST, + port: Number(env.IPFS_PORT || 5001), + protocol: env.IPFS_PROTOCOL || 'http', + } : null, }; } diff --git a/src/db/appDatabase.js b/src/db/appDatabase.js index 1eb0217..ab677de 100644 --- a/src/db/appDatabase.js +++ b/src/db/appDatabase.js @@ -32,11 +32,27 @@ class AppDatabase { CREATE TABLE IF NOT EXISTS videos ( id TEXT PRIMARY KEY, creator_id TEXT NOT NULL REFERENCES creators(id), - title TEXT, - visibility TEXT NOT NULL, + title TEXT NOT NULL, + description TEXT, + original_filename TEXT NOT NULL, + file_path TEXT NOT NULL, + file_size INTEGER NOT NULL, + status TEXT NOT NULL DEFAULT 'uploaded', + message TEXT, + visibility TEXT NOT NULL DEFAULT 'private', + created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); + CREATE TABLE IF NOT EXISTS transcoding_results ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + video_id TEXT NOT NULL REFERENCES videos(id), + master_playlist TEXT NOT NULL, + resolutions TEXT NOT NULL, + upload_results TEXT, + created_at TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS coop_splits ( id TEXT PRIMARY KEY, creator_id TEXT NOT NULL REFERENCES creators(id), diff --git a/src/services/videoProcessingWorker.js b/src/services/videoProcessingWorker.js new file mode 100644 index 0000000..8a13033 --- /dev/null +++ b/src/services/videoProcessingWorker.js @@ -0,0 +1,212 @@ +const Queue = require('bull'); +const redis = require('redis'); +const VideoTranscodingService = require('./videoTranscodingService'); +const { AppDatabase } = require('../db/appDatabase'); + +class VideoProcessingWorker { + constructor(config, database) { + this.config = config; + this.database = database; + this.redis = redis.createClient(config.redis); + this.transcodingService = new VideoTranscodingService(config); + + this.processingQueue = new Queue('video processing', { + redis: config.redis, + defaultJobOptions: { + removeOnComplete: 10, + removeOnFail: 5, + attempts: 3, + backoff: { + type: 'exponential', + delay: 2000 + } + } + }); + + this.setupProcessors(); + this.setupEventListeners(); + } + + setupProcessors() { + this.processingQueue.process('transcode-video', async (job) => { + const { videoId, inputPath, options } = job.data; + + try { + await this.updateVideoStatus(videoId, 'processing', 'Starting transcoding...'); + + const result = await this.transcodingService.transcodeToHLS(inputPath, videoId, options); + + await this.updateVideoStatus(videoId, 'completed', 'Transcoding completed successfully'); + await this.saveTranscodingResult(videoId, result); + + return result; + } catch (error) { + await this.updateVideoStatus(videoId, 'failed', `Transcoding failed: ${error.message}`); + throw error; + } + }); + + this.processingQueue.process('adaptive-bitrate-analysis', async (job) => { + const { videoId, connectionMetrics } = job.data; + + const recommendedQuality = this.analyzeConnectionQuality(connectionMetrics); + + await this.updateRecommendedQuality(videoId, recommendedQuality); + + return { videoId, recommendedQuality }; + }); + } + + setupEventListeners() { + this.processingQueue.on('completed', (job, result) => { + console.log(`Job ${job.id} completed:`, result); + }); + + this.processingQueue.on('failed', (job, err) => { + console.error(`Job ${job.id} failed:`, err); + }); + + this.processingQueue.on('progress', (job, progress) => { + console.log(`Job ${job.id} progress: ${progress}%`); + }); + } + + async addTranscodingJob(videoId, inputPath, options = {}) { + const job = await this.processingQueue.add('transcode-video', { + videoId, + inputPath, + options + }, { + priority: options.priority || 'normal' + }); + + await this.updateVideoStatus(videoId, 'queued', 'Video queued for transcoding'); + + return job; + } + + async addAdaptiveBitrateJob(videoId, connectionMetrics) { + const job = await this.processingQueue.add('adaptive-bitrate-analysis', { + videoId, + connectionMetrics + }, { + delay: 0, + priority: 'high' + }); + + return job; + } + + analyzeConnectionQuality(metrics) { + const { bandwidth, latency, packetLoss } = metrics; + + if (bandwidth < 1 || packetLoss > 0.05) { + return '360p'; + } else if (bandwidth < 3 || latency > 200) { + return '720p'; + } else { + return '1080p'; + } + } + + async updateVideoStatus(videoId, status, message) { + try { + await this.database.run( + 'UPDATE videos SET status = ?, message = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', + [status, message, videoId] + ); + + await this.redis.setex(`video:${videoId}:status`, 3600, JSON.stringify({ + status, + message, + timestamp: new Date().toISOString() + })); + } catch (error) { + console.error('Failed to update video status:', error); + } + } + + async saveTranscodingResult(videoId, result) { + try { + await this.database.run( + `INSERT OR REPLACE INTO transcoding_results + (video_id, master_playlist, resolutions, upload_results, created_at) + VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)`, + [ + videoId, + JSON.stringify(result.masterPlaylist), + JSON.stringify(result.resolutions), + JSON.stringify(result.uploadResults) + ] + ); + } catch (error) { + console.error('Failed to save transcoding result:', error); + } + } + + async updateRecommendedQuality(videoId, quality) { + try { + await this.redis.setex(`video:${videoId}:quality`, 1800, quality); + } catch (error) { + console.error('Failed to update recommended quality:', error); + } + } + + async getRecommendedQuality(videoId) { + try { + const quality = await this.redis.get(`video:${videoId}:quality`); + return quality || '720p'; + } catch (error) { + console.error('Failed to get recommended quality:', error); + return '720p'; + } + } + + async getVideoStatus(videoId) { + try { + const status = await this.redis.get(`video:${videoId}:status`); + return status ? JSON.parse(status) : null; + } catch (error) { + console.error('Failed to get video status:', error); + return null; + } + } + + async getQueueStats() { + try { + const waiting = await this.processingQueue.getWaiting(); + const active = await this.processingQueue.getActive(); + const completed = await this.processingQueue.getCompleted(); + const failed = await this.processingQueue.getFailed(); + + return { + waiting: waiting.length, + active: active.length, + completed: completed.length, + failed: failed.length + }; + } catch (error) { + console.error('Failed to get queue stats:', error); + return null; + } + } + + async pauseQueue() { + await this.processingQueue.pause(); + } + + async resumeQueue() { + await this.processingQueue.resume(); + } + + async gracefulShutdown() { + console.log('Shutting down video processing worker...'); + + await this.processingQueue.close(); + await this.redis.quit(); + + console.log('Video processing worker shut down successfully'); + } +} + +module.exports = VideoProcessingWorker; diff --git a/src/services/videoTranscodingService.js b/src/services/videoTranscodingService.js new file mode 100644 index 0000000..4cb7095 --- /dev/null +++ b/src/services/videoTranscodingService.js @@ -0,0 +1,199 @@ +const ffmpeg = require('fluent-ffmpeg'); +const path = require('path'); +const fs = require('fs').promises; +const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3'); +const { create } = require('ipfs-http-client'); + +class VideoTranscodingService { + constructor(config) { + this.config = config; + this.s3Client = config.s3 ? new S3Client(config.s3) : null; + this.ipfsClient = config.ipfs ? create(config.ipfs) : null; + this.outputDir = config.transcoding?.outputDir || './transcoded'; + this.resolutions = [ + { name: '360p', width: 640, height: 360, bitrate: '800k' }, + { name: '720p', width: 1280, height: 720, bitrate: '2500k' }, + { name: '1080p', width: 1920, height: 1080, bitrate: '5000k' } + ]; + } + + async transcodeToHLS(inputPath, videoId, options = {}) { + try { + await fs.mkdir(this.outputDir, { recursive: true }); + const videoDir = path.join(this.outputDir, videoId); + await fs.mkdir(videoDir, { recursive: true }); + + const transcodingPromises = this.resolutions.map(resolution => + this.transcodeResolution(inputPath, videoDir, videoId, resolution) + ); + + const transcodedFiles = await Promise.all(transcodingPromises); + + const masterPlaylist = await this.generateMasterPlaylist(videoDir, videoId, transcodedFiles); + + const uploadResults = await this.uploadToStorage(videoDir, videoId); + + await this.cleanupLocalFiles(videoDir); + + return { + videoId, + masterPlaylist, + resolutions: transcodedFiles, + uploadResults + }; + } catch (error) { + throw new Error(`Transcoding failed: ${error.message}`); + } + } + + async transcodeResolution(inputPath, outputDir, videoId, resolution) { + const playlistPath = path.join(outputDir, `${resolution.name}.m3u8`); + const segmentPattern = path.join(outputDir, `${resolution.name}_%03d.ts`); + + return new Promise((resolve, reject) => { + ffmpeg(inputPath) + .videoCodec('libx264') + .audioCodec('aac') + .size(`${resolution.width}x${resolution.height}`) + .videoBitrate(resolution.bitrate) + .audioBitrate('128k') + .format('hls') + .addOption('-hls_time', '10') + .addOption('-hls_list_size', '0') + .addOption('-hls_segment_filename', segmentPattern) + .addOption('-f', 'hls') + .output(playlistPath) + .on('end', () => { + resolve({ + resolution: resolution.name, + playlist: `${resolution.name}.m3u8`, + segments: `${resolution.name}_%03d.ts`, + bitrate: resolution.bitrate, + width: resolution.width, + height: resolution.height + }); + }) + .on('error', (error) => { + reject(new Error(`Failed to transcode ${resolution.name}: ${error.message}`)); + }) + .run(); + }); + } + + async generateMasterPlaylist(outputDir, videoId, transcodedFiles) { + const masterPlaylistPath = path.join(outputDir, 'master.m3u8'); + + let playlist = '#EXTM3U\n#EXT-X-VERSION:6\n\n'; + + transcodedFiles.forEach(file => { + playlist += `#EXT-X-STREAM-INF:BANDWIDTH=${this.getBandwidth(file.bitrate)},RESOLUTION=${file.width}x${file.height}\n`; + playlist += `${file.playlist}\n\n`; + }); + + await fs.writeFile(masterPlaylistPath, playlist); + + return { + filename: 'master.m3u8', + content: playlist, + url: this.getPlaylistUrl(videoId, 'master.m3u8') + }; + } + + getBandwidth(bitrate) { + const numericBitrate = parseInt(bitrate.replace('k', '')) * 1000; + return numericBitrate + 128000; + } + + getPlaylistUrl(videoId, filename) { + if (this.config.cdn?.baseUrl) { + return `${this.config.cdn.baseUrl}/${videoId}/${filename}`; + } + return `/videos/${videoId}/${filename}`; + } + + async uploadToStorage(videoDir, videoId) { + const results = { s3: null, ipfs: null }; + const files = await fs.readdir(videoDir); + + if (this.s3Client) { + results.s3 = await this.uploadToS3(videoDir, videoId, files); + } + + if (this.ipfsClient) { + results.ipfs = await this.uploadToIPFS(videoDir, videoId, files); + } + + return results; + } + + async uploadToS3(videoDir, videoId, files) { + const uploadPromises = files.map(async (file) => { + const filePath = path.join(videoDir, file); + const fileContent = await fs.readFile(filePath); + + const command = new PutObjectCommand({ + Bucket: this.config.s3.bucket, + Key: `videos/${videoId}/${file}`, + Body: fileContent, + ContentType: this.getContentType(file) + }); + + await this.s3Client.send(command); + return { file, key: `videos/${videoId}/${file}` }; + }); + + return Promise.all(uploadPromises); + } + + async uploadToIPFS(videoDir, videoId, files) { + const uploadPromises = files.map(async (file) => { + const filePath = path.join(videoDir, file); + const fileContent = await fs.readFile(filePath); + + const result = await this.ipfsClient.add(fileContent); + return { file, hash: result.path, url: `ipfs://${result.path}` }; + }); + + return Promise.all(uploadPromises); + } + + getContentType(filename) { + const ext = path.extname(filename).toLowerCase(); + const contentTypes = { + '.m3u8': 'application/vnd.apple.mpegurl', + '.ts': 'video/mp2t' + }; + return contentTypes[ext] || 'application/octet-stream'; + } + + async cleanupLocalFiles(videoDir) { + try { + await fs.rmdir(videoDir, { recursive: true }); + } catch (error) { + console.warn(`Failed to cleanup local files: ${error.message}`); + } + } + + async getTranscodingStatus(videoId) { + const statusFile = path.join(this.outputDir, `${videoId}_status.json`); + try { + const content = await fs.readFile(statusFile, 'utf8'); + return JSON.parse(content); + } catch (error) { + return null; + } + } + + async updateTranscodingStatus(videoId, status) { + const statusFile = path.join(this.outputDir, `${videoId}_status.json`); + const statusData = { + videoId, + status, + timestamp: new Date().toISOString(), + ...status + }; + await fs.writeFile(statusFile, JSON.stringify(statusData, null, 2)); + } +} + +module.exports = VideoTranscodingService; diff --git a/test-video-transcoding.js b/test-video-transcoding.js new file mode 100644 index 0000000..a662015 --- /dev/null +++ b/test-video-transcoding.js @@ -0,0 +1,108 @@ +const request = require('supertest'); +const path = require('path'); +const fs = require('fs'); +const { createApp } = require('./index'); + +async function testVideoTranscoding() { + console.log('๐Ÿงช Testing Video Transcoding API...\n'); + + // Mock configuration for testing + const testConfig = { + port: 3001, + auth: { + creatorJwtSecret: 'test-secret', + issuer: 'test-issuer', + audience: 'test-audience', + }, + database: { + filename: ':memory:', + }, + cdn: { + baseUrl: 'http://localhost:3001', + tokenSecret: 'test-cdn-secret', + tokenTtlSeconds: 300, + }, + soroban: { + rpcUrl: '', + networkPassphrase: 'Test Network', + contractId: 'test-contract', + sourceSecret: '', + method: 'has_active_subscription', + }, + transcoding: { + ffmpegPath: 'ffmpeg', + outputDir: './test-transcoded', + maxConcurrent: 1, + }, + redis: { + host: 'localhost', + port: 6379, + password: '', + db: 1, // Use test DB + }, + s3: null, // Disable S3 for testing + ipfs: null, // Disable IPFS for testing + }; + + const app = createApp({ config: testConfig }); + + try { + // Test 1: Health check + console.log('1. Testing health check...'); + const healthResponse = await request(app) + .get('/') + .expect(200); + + console.log('โœ… Health check passed:', healthResponse.body.project); + + // Test 2: Queue stats + console.log('\n2. Testing queue stats...'); + const queueResponse = await request(app) + .get('/api/videos/queue/stats') + .expect(200); + + console.log('โœ… Queue stats:', queueResponse.body.data); + + // Test 3: Video status for non-existent video + console.log('\n3. Testing video status (non-existent)...'); + const statusResponse = await request(app) + .get('/api/videos/non-existent-video/status') + .expect(404); + + console.log('โœ… Correctly returns 404 for non-existent video'); + + // Test 4: Playlist for non-existent video + console.log('\n4. Testing playlist (non-existent video)...'); + const playlistResponse = await request(app) + .get('/api/videos/non-existent-video/playlist') + .expect(404); + + console.log('โœ… Correctly returns 404 for non-existent playlist'); + + console.log('\n๐ŸŽ‰ All basic tests passed!'); + console.log('\n๐Ÿ“ Note: Full video transcoding tests require:'); + console.log(' - FFmpeg installation'); + console.log(' - Redis server'); + console.log(' - Actual video files'); + console.log('\n๐Ÿš€ To test with real video upload:'); + console.log(' 1. Install FFmpeg and start Redis'); + console.log(' 2. Set up environment variables'); + console.log(' 3. Use curl or Postman to upload a video file'); + + } catch (error) { + console.error('โŒ Test failed:', error.message); + process.exit(1); + } +} + +// Run tests if this file is executed directly +if (require.main === module) { + testVideoTranscoding() + .then(() => process.exit(0)) + .catch((error) => { + console.error('Test suite failed:', error); + process.exit(1); + }); +} + +module.exports = { testVideoTranscoding }; From 01445d906bbdeecdd28266c8d5115858720b8f0e Mon Sep 17 00:00:00 2001 From: lifewithbigdamz Date: Wed, 25 Mar 2026 00:25:12 +0100 Subject: [PATCH 3/4] feat: Implement comprehensive video transcoding and streaming system - Add VideoTranscodingService with FFmpeg-based HLS transcoding - Support multiple resolutions (360p, 720p, 1080p) for adaptive streaming - Implement VideoProcessingWorker with Redis-based job queue - Create video upload and streaming API endpoints - Add IPFS and S3 storage integration for transcoded segments - Build adaptive bitrate streaming logic for connection-based quality selection - Update database schema for video metadata and transcoding results - Fix CI/CD pipeline with Node.js 20.11.0+ and FFmpeg support - Add comprehensive API documentation and test suite - Integrate with existing pay-per-second subscription system Resolves: Users on different internet speeds need smooth viewing experience --- package-lock.json | 559 +++++++++++++++++++++++++++++++++++++++++- src/db/appDatabase.js | 20 +- 2 files changed, 568 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0f2fc87..cbcb56d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,23 +9,31 @@ "version": "1.0.0", "dependencies": { "@stellar/stellar-sdk": "^14.6.1", + "aws-sdk": "^2.1500.0", "axios": "^1.6.2", "better-sqlite3": "^12.8.0", + "bull": "^4.12.2", "cors": "^2.8.6", "dotenv": "^17.3.1", "ethers": "^6.8.1", "express": "^5.2.1", + "fluent-ffmpeg": "^2.1.2", "form-data": "^4.0.0", "ipfs-http-client": "^60.0.1", "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", + "redis": "^4.6.10", "stellar-sdk": "^13.3.0", + "uuid": "^9.0.1", "web3.storage": "^4.5.4" }, "devDependencies": { "jest": "^29.7.0", "nodemon": "^3.0.2", "supertest": "^7.0.0" + }, + "engines": { + "node": ">=20.11.0" } }, "node_modules/@adraffy/ens-normalize": { @@ -571,6 +579,12 @@ "node": ">=14" } }, + "node_modules/@ioredis/commands": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz", + "integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==", + "license": "MIT" + }, "node_modules/@ipld/car": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@ipld/car/-/car-3.2.4.tgz", @@ -1390,6 +1404,84 @@ "npm": ">=7.0.0" } }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@multiformats/dns": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/@multiformats/dns/-/dns-1.0.13.tgz", @@ -1643,6 +1735,71 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause" }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", + "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.10", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", @@ -2074,6 +2231,11 @@ "dev": true, "license": "MIT" }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2095,6 +2257,70 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sdk": { + "version": "2.1693.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1693.0.tgz", + "integrity": "sha512-cJmb8xEnVLT+R6fBS5sn/EFJiX7tUnDaPtOPZ1vFbOJtd0fnZn/Ky2XGgsvvoeliWeH7mL3TWSX5zXXGSQV6gQ==", + "deprecated": "The AWS SDK for JavaScript (v2) has reached end-of-support, and no longer receives updates. Please migrate your code to use AWS SDK for JavaScript (v3). More info https://a.co/cUPnyil", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/aws-sdk/node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/aws-sdk/node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "license": "BSD-3-Clause" + }, + "node_modules/aws-sdk/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/axios": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", @@ -2544,6 +2770,45 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/bull": { + "version": "4.16.5", + "resolved": "https://registry.npmjs.org/bull/-/bull-4.16.5.tgz", + "integrity": "sha512-lDsx2BzkKe7gkCYiT5Acj02DpTwDznl/VNN7Psn7M3USPG7Vs/BaClZJJTAG+ufAR9++N1/NiUTdaFBWDIl5TQ==", + "license": "MIT", + "dependencies": { + "cron-parser": "^4.9.0", + "get-port": "^5.1.1", + "ioredis": "^5.3.2", + "lodash": "^4.17.21", + "msgpackr": "^1.11.2", + "semver": "^7.5.2", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/bull/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/bull/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -2813,6 +3078,15 @@ "node": ">=12" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -3028,6 +3302,18 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "license": "MIT", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3186,6 +3472,15 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "license": "MIT", @@ -3798,6 +4093,32 @@ "node": ">=8" } }, + "node_modules/fluent-ffmpeg": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", + "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "dependencies": { + "async": "^0.2.9", + "which": "^1.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/fluent-ffmpeg/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/follow-redirects": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", @@ -3946,6 +4267,15 @@ "node": ">= 0.4" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -4004,6 +4334,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-proto": { "version": "1.0.1", "license": "MIT", @@ -4412,6 +4754,30 @@ "integrity": "sha512-+WvfEZnFUhRwFxgz+QCQi7UC6o9AM0EHM9bpIe2Nhqb100NHCsTvNAn4eJgvgV2/tmLo1MP9nGxQKEcZTAueLA==", "license": "Apache-2.0 OR MIT" }, + "node_modules/ioredis": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz", + "integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "1.5.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ip-regex": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", @@ -5419,7 +5785,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/iso-random-stream": { @@ -6265,6 +6630,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6485,12 +6859,30 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT" }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -6543,6 +6935,15 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/main-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/main-event/-/main-event-1.0.1.tgz", @@ -6860,6 +7261,37 @@ "version": "2.1.3", "license": "MIT" }, + "node_modules/msgpackr": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.9.tgz", + "integrity": "sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, "node_modules/multer": { "version": "1.4.5-lts.2", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", @@ -7110,6 +7542,21 @@ "node": ">= 6.13.0" } }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -7742,6 +8189,12 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "license": "MIT" + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -7772,6 +8225,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -8002,6 +8464,44 @@ "node": ">=8" } }, + "node_modules/redis": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", + "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.1", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/require-addon": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", @@ -8148,6 +8648,12 @@ "version": "2.1.2", "license": "MIT" }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "license": "ISC" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -8511,6 +9017,12 @@ "node": ">=10" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.2", "license": "MIT", @@ -9131,6 +9643,16 @@ "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", "license": "MIT" }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, "node_modules/utf8-codec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/utf8-codec/-/utf8-codec-1.0.0.tgz", @@ -9156,6 +9678,19 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -9451,6 +9986,28 @@ } } }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/src/db/appDatabase.js b/src/db/appDatabase.js index ab677de..fd76dc0 100644 --- a/src/db/appDatabase.js +++ b/src/db/appDatabase.js @@ -7,6 +7,7 @@ const Database = require('better-sqlite3'); * SQLite-backed application database wrapper. */ class AppDatabase { + // ...existing code... /** * @param {string} filename SQLite filename or `:memory:`. */ @@ -83,7 +84,6 @@ class AppDatabase { CREATE INDEX IF NOT EXISTS idx_creator_audit_logs_creator_timestamp ON creator_audit_logs (creator_id, timestamp DESC); - ON creator_audit_logs (creator_id, timestamp DESC); CREATE TABLE IF NOT EXISTS comments ( id TEXT PRIMARY KEY, @@ -429,8 +429,10 @@ class AppDatabase { 'SELECT creator_id AS creatorId, wallet_address AS walletAddress, active, subscribed_at AS subscribedAt, unsubscribed_at AS unsubscribedAt FROM subscriptions WHERE creator_id = ? AND wallet_address = ?', ) .get(creatorId, walletAddress); + return row || null; + } + /** * Create a new comment. - * * @param {{postId: string, userAddress: string, creatorId: string, content: string}} comment Comment data. * @returns {object} */ @@ -554,8 +556,10 @@ class AppDatabase { .prepare('SELECT COUNT(1) AS ct FROM subscriptions WHERE creator_id = ? AND active = 1') .get(creatorId); return (row && Number(row.ct)) || 0; + } + + /** * Get comments by post ID. - * * @param {string} postId Post identifier. * @returns {object[]} */ @@ -567,14 +571,13 @@ class AppDatabase { FROM comments WHERE post_id = ? ORDER BY created_at DESC - `, + ` ) .all(postId); } /** * Update a comment. - * * @param {{commentId: string, content: string}} input Update payload. * @returns {object} */ @@ -586,16 +589,14 @@ class AppDatabase { UPDATE comments SET content = ?, updated_at = ? WHERE id = ? - `, + ` ) .run(input.content, now, input.commentId); - return this.getCommentById(input.commentId); } /** * Delete a comment. - * * @param {string} commentId Comment identifier. * @returns {boolean} */ @@ -604,10 +605,9 @@ class AppDatabase { .prepare( ` DELETE FROM comments WHERE id = ? - `, + ` ) .run(commentId); - return result.changes > 0; } } From 37d51a00ef6ea3b5d52b5b45e0dbb7369bd57166 Mon Sep 17 00:00:00 2001 From: Skinny001 Date: Wed, 25 Mar 2026 13:20:27 +0100 Subject: [PATCH 4/4] WIP: save local changes before rebase :wq :qw :qw :wq --- src/db/appDatabase.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/db/appDatabase.js b/src/db/appDatabase.js index fd76dc0..f859193 100644 --- a/src/db/appDatabase.js +++ b/src/db/appDatabase.js @@ -426,7 +426,7 @@ class AppDatabase { getSubscription(creatorId, walletAddress) { const row = this.db .prepare( - 'SELECT creator_id AS creatorId, wallet_address AS walletAddress, active, subscribed_at AS subscribedAt, unsubscribed_at AS unsubscribedAt FROM subscriptions WHERE creator_id = ? AND wallet_address = ?', + 'SELECT creator_id AS creatorId, wallet_address AS walletAddress, active, subscribed_at AS subscribedAt, unsubscribed_at AS unsubscribedAt FROM subscriptions WHERE creator_id = ? AND wallet_address = ?' ) .get(creatorId, walletAddress); return row || null; @@ -444,16 +444,14 @@ class AppDatabase { ` INSERT INTO comments (id, post_id, user_address, creator_id, content, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?) - `, + ` ) .run(id, comment.postId, comment.userAddress, comment.creatorId, comment.content, now, now); - return this.getCommentById(id); } /** * Get a comment by ID. - * * @param {string} commentId Comment identifier. * @returns {object|null} */ @@ -464,10 +462,9 @@ class AppDatabase { SELECT id, post_id AS postId, user_address AS userAddress, creator_id AS creatorId, content, created_at AS createdAt, updated_at AS updatedAt FROM comments WHERE id = ? - `, + ` ) .get(commentId); - return row || null; }