diff --git a/services/our/README.md b/services/our/README.md index ee717f9b..cf29bc27 100644 --- a/services/our/README.md +++ b/services/our/README.md @@ -45,7 +45,7 @@ Start node app in dev mode. Env vars must be available to the app-- We're using * [x] VTubers * [ ] Tags * [ ] Toys -* [ ] Monetized affiliate links +* [ ] Monetized affiliate links (https://www.viglink.com maybe?) ## Tiers & Privs diff --git a/services/pocketbase/package.json b/services/pocketbase/package.json index ad45e587..951fa2f5 100644 --- a/services/pocketbase/package.json +++ b/services/pocketbase/package.json @@ -1,6 +1,6 @@ { "name": "futureporn", - "version": "3.5.0", + "version": "3.5.1", "private": true, "description": "Dedication to the preservation of lewdtuber history", "license": "Unlicense", diff --git a/services/pocketbase/pb_hooks/pages/(site)/+layout.ejs b/services/pocketbase/pb_hooks/pages/(site)/+layout.ejs index 277c71bd..2b966f64 100644 --- a/services/pocketbase/pb_hooks/pages/(site)/+layout.ejs +++ b/services/pocketbase/pb_hooks/pages/(site)/+layout.ejs @@ -83,8 +83,8 @@

To watch a vod, please log in.

<% } %> -
-

Torrent downloads are coming soon.

+
+

Torrents are in the process of being added to the site.

Futureporn is no longer using IPFS. Read more

diff --git a/services/pocketbase/pb_hooks/pages/(site)/vods/[id]/index.ejs b/services/pocketbase/pb_hooks/pages/(site)/vods/[id]/index.ejs index 60f96349..2c2f3d7f 100644 --- a/services/pocketbase/pb_hooks/pages/(site)/vods/[id]/index.ejs +++ b/services/pocketbase/pb_hooks/pages/(site)/vods/[id]/index.ejs @@ -41,11 +41,13 @@ diff --git a/services/pocketbase/pb_hooks/pages/_private/vod-list.ejs b/services/pocketbase/pb_hooks/pages/_private/vod-list.ejs index b8ef5731..e13088ff 100644 --- a/services/pocketbase/pb_hooks/pages/_private/vod-list.ejs +++ b/services/pocketbase/pb_hooks/pages/_private/vod-list.ejs @@ -6,6 +6,7 @@ Stream Date VTuber Thumbnail + DL @@ -37,6 +38,18 @@ No thumbnail <% } %> + + <% if (vod?.magnetLink) { %> + + + + + + + + + <% } %> + <% } %> diff --git a/services/pocketbase/pb_hooks/pages/vods/rss.xml/index.ejs b/services/pocketbase/pb_hooks/pages/vods/rss.xml/index.ejs index 1f1bfa20..9896bcf6 100644 --- a/services/pocketbase/pb_hooks/pages/vods/rss.xml/index.ejs +++ b/services/pocketbase/pb_hooks/pages/vods/rss.xml/index.ejs @@ -1,18 +1,22 @@ <%# index.ejs — RSS Feed Generator (RSS 2.0) -Expects: data.vods = [{ id, title, notes, thumbnail, streamDate }] +Expects: data.vods = [{ id, title, notes, thumbnail, streamDate, magnetLink }] +When in doubt, copy https://nyaa.si/?page=rss %> <% -response.header("Content-Type", "application/rss+xml") +response.header("Content-Type", "application/rss+xml"); -// Build RSS XML as a string let rss = ` - + + Futureporn.net VODs https://futureporn.net - Dedication to the preservaton of lewdtuber history + Dedication to the preservation of lewdtuber history ${new Date().toUTCString()} https://validator.w3.org/feed/docs/rss2.html pocketpages @@ -22,26 +26,42 @@ let rss = ` https://futureporn.net/images/futureporn-icon.png https://futureporn.net `; - + for (const vod of data.vods) { - const url = `${env('ORIGIN')}/vods/${vod.id}`; + const vodId = vod.get('id'); + const title = + (vod?.get('expand')?.vtubers?.map(v => v.get('displayName')).join(', ')) + || vod.get('streamDate'); + + const vodUrl = `${env('ORIGIN')}/vods/${vodId}`; + const pubDate = new Date(vod.get('streamDate')).toUTCString(); + const notes = vod.get('notes'); + const thumbnail = vod.get('thumbnail'); + const magnetLink = vod.get('magnetLink'); + + // @TODO the must contain a .torrent, NOT a magnetLink. rss += ` + <![CDATA[${title}]]> + ${vodUrl} + ${vodUrl} + ${vodUrl} + ${pubDate}`; - <![CDATA[${ - ((vod?.get('expand')?.vtubers?.map(vt => vt.get('displayName')) || []).join(', ') - || vod.get('streamDate')) - }]]> - ${url} - ${url} - ${new Date(vod.get('streamDate')).toUTCString()}`; - if (vod.get('notes')) { + // Description + if (notes) { rss += ` - `; + ${vodUrl} ${notes}]]>`; } - if (vod.get('thumbnail')) { - rss += `${env('ORIGIN')}/api/files/vods/${vod.get('id')}/${vod.get('thumbnail')}` + + // Thumbnail (custom tag — allowed) + if (thumbnail) { + rss += ` + ${env('ORIGIN')}/api/files/vods/${vodId}/${thumbnail}`; } + + + rss += ` `; } @@ -50,6 +70,5 @@ rss += ` `; -// Send the RSS response.html(200, rss); %> \ No newline at end of file diff --git a/services/worker/.config/env.ts b/services/worker/.config/env.ts index 51bb9a30..b83a2dc1 100644 --- a/services/worker/.config/env.ts +++ b/services/worker/.config/env.ts @@ -1,3 +1,5 @@ +import envPaths from 'env-paths'; +const paths = envPaths('futureporn', { suffix: '' }); const env = (() => { if (!process.env.POCKETBASE_URL) throw new Error('POCKETBASE_URL missing in env'); @@ -27,7 +29,6 @@ const env = (() => { if (!process.env.FANSLY_PASSWORD) throw new Error('FANSLY_PASSWORD missing in env'); if (!process.env.APIFY_TOKEN) throw new Error('APIFY_TOKEN missing in env'); if (!process.env.NODE_ENV) throw new Error('APIFY_TOKEN missing in env'); - if (!process.env.CACHE_ROOT) throw new Error('CACHE_ROOT missing in env'); if (!process.env.SEEDBOX_SFTP_HOST) throw new Error('SEEDBOX_SFTP_HOST missing in env'); if (!process.env.SEEDBOX_SFTP_PORT) throw new Error('SEEDBOX_SFTP_PORT missing in env'); if (!process.env.SEEDBOX_SFTP_USERNAME) throw new Error('SEEDBOX_SFTP_USERNAME missing in env'); @@ -37,6 +38,10 @@ const env = (() => { if (!process.env.QBT_PASSWORD) throw new Error('QBT_PASSWORD missing in env'); if (!process.env.QBT_USERNAME) throw new Error('QBT_USERNAME missing in env'); if (!process.env.WORKER_WORKERS) throw new Error('WORKER_WORKERS missing in env'); + if (!process.env.BUNNY_ZONE_URL) throw new Error('BUNNY_ZONE_URL missing in env'); + if (!process.env.BUNNY_TOKEN_KEY) throw new Error('BUNNY_TOKEN_KEY missing in env'); + + const CACHE_ROOT = process.env?.CACHE_ROOT || paths.cache; const { PORT, @@ -65,7 +70,6 @@ const env = (() => { FANSLY_PASSWORD, APIFY_TOKEN, NODE_ENV, - CACHE_ROOT, SEEDBOX_SFTP_HOST, SEEDBOX_SFTP_PORT, SEEDBOX_SFTP_USERNAME, @@ -76,8 +80,11 @@ const env = (() => { QBT_PASSWORD, QBT_PORT, WORKER_WORKERS, + BUNNY_ZONE_URL, + BUNNY_TOKEN_KEY, } = process.env return { + CACHE_ROOT, PORT, WORKER_PORT, POCKETBASE_URL, @@ -104,7 +111,6 @@ const env = (() => { FANSLY_USERNAME, APIFY_TOKEN, NODE_ENV, - CACHE_ROOT, SEEDBOX_SFTP_HOST, SEEDBOX_SFTP_PORT, SEEDBOX_SFTP_USERNAME, @@ -115,6 +121,8 @@ const env = (() => { QBT_PASSWORD, QBT_PORT, WORKER_WORKERS, + BUNNY_ZONE_URL, + BUNNY_TOKEN_KEY, } })() diff --git a/services/worker/package-lock.json b/services/worker/package-lock.json index 3389c8da..c5b3ac2e 100644 --- a/services/worker/package-lock.json +++ b/services/worker/package-lock.json @@ -1,12 +1,12 @@ { "name": "worker", - "version": "0.0.1", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "worker", - "version": "0.0.1", + "version": "0.1.0", "dependencies": { "@bull-board/express": "^6.14.0", "@mux/mux-node": "^12.8.0", @@ -15,7 +15,7 @@ "@types/node": "^24.10.1", "@types/semver": "^7.7.1", "@types/ssh2": "^1.15.5", - "apify-client": "^2.19.0", + "apify-client": "^2.20.0", "bullmq": "^5.63.0", "date-fns": "^4.1.0", "fs-extra": "^11.3.2", @@ -24,14 +24,14 @@ "nanoid": "^5.1.6", "onnxruntime-web": "^1.23.2", "pocketbase": "^0.26.3", - "puppeteer": "^24.30.0", + "puppeteer": "^24.31.0", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "semver": "^7.7.3", "sharp": "^0.34.5", "slugify": "^1.6.6", "ssh2": "^1.17.0", - "which": "^5.0.0" + "which": "^6.0.0" }, "devDependencies": { "tsx": "^4.20.6", @@ -58,9 +58,9 @@ } }, "node_modules/@apify/utilities": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/@apify/utilities/-/utilities-2.23.0.tgz", - "integrity": "sha512-WAyenlKvtXtvd6V8D2fYwbsmc3dMn3z02JaOhZNx/p8u0NuacNgoLk/+PW4IPWrNxhqgDQZqde4cpNfZOktvsQ==", + "version": "2.23.2", + "resolved": "https://registry.npmjs.org/@apify/utilities/-/utilities-2.23.2.tgz", + "integrity": "sha512-kNA9m7DN+6CW6OEadH43CZPz5dJ1UfimfXtkt2Oq0yU7ovN9kNZXOGrpGPGMxVwKmIeIghZEzMriuI6XVAFtfA==", "license": "Apache-2.0", "dependencies": { "@apify/consts": "^2.47.0", @@ -91,36 +91,36 @@ } }, "node_modules/@bull-board/api": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-6.14.0.tgz", - "integrity": "sha512-oMDwXwoPn0RsdZ3Y68/bOErZ/qGZE5H97vgE/Pc8Uul/OHajlvajKW4NV+ZGTix82liUfH9CkjYx7PpwvBWhxg==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-6.14.2.tgz", + "integrity": "sha512-UzkvN/wM+1qS73BS43a75LYkRzpBpCCUKlaGq0hp3dM5MNmdF1mx7LMGYgXPt91gqF8j4jq9Y/zCpC3Sqs3RLQ==", "license": "MIT", "dependencies": { "redis-info": "^3.1.0" }, "peerDependencies": { - "@bull-board/ui": "6.14.0" + "@bull-board/ui": "6.14.2" } }, "node_modules/@bull-board/express": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-6.14.0.tgz", - "integrity": "sha512-3H1ame2G1+eVnqqSsw6KfzTGYAWSpVsIx6EPwg9vPSP2eKfNAm12Cm4zvL6ZkwAvTCkAByt5PPDRWbbwWB6HHQ==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-6.14.2.tgz", + "integrity": "sha512-nghb4MpYDodYZpeiZvI9tXFDHqiAXE8FhrLOFDkuQL0GBhw0gEOuGSISjdKrnFDAW72LWVq0XfGKWYD8V5nF0w==", "license": "MIT", "dependencies": { - "@bull-board/api": "6.14.0", - "@bull-board/ui": "6.14.0", + "@bull-board/api": "6.14.2", + "@bull-board/ui": "6.14.2", "ejs": "^3.1.10", "express": "^4.21.1 || ^5.0.0" } }, "node_modules/@bull-board/ui": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-6.14.0.tgz", - "integrity": "sha512-5yqfS9CwWR8DBxpReIbqv/VSPFM/zT4KZ75keyApMiejasRC2joaHqEzYWlMCjkMycbNNCvlQNlTbl+C3dE/dg==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-6.14.2.tgz", + "integrity": "sha512-OTCsBbMAhYoB2NJc6FxkkREWWPUFvEhL2Az1gAKpdNOBqup4CsKj7eBK3rcWSRLZ4LnaOaPK8E8tiogkhrRuOA==", "license": "MIT", "dependencies": { - "@bull-board/api": "6.14.0" + "@bull-board/api": "6.14.2" } }, "node_modules/@crawlee/types": { @@ -136,9 +136,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", - "integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", "license": "MIT", "optional": true, "dependencies": { @@ -1054,6 +1054,8 @@ }, "node_modules/@ioredis/commands": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", + "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==", "license": "MIT" }, "node_modules/@jridgewell/sourcemap-codec": { @@ -1063,8 +1065,62 @@ "dev": true, "license": "MIT" }, + "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" ], @@ -1074,8 +1130,23 @@ "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/@mux/mux-node": { "version": "12.8.0", + "resolved": "https://registry.npmjs.org/@mux/mux-node/-/mux-node-12.8.0.tgz", + "integrity": "sha512-J7Qe1JGlOWle+5huN27hBcAKujx0xgC8d3qQ1s9emyEETR+ubxlRf/9UOlN+GoPtoMPIDa5fWDRf2LDWzsiBOA==", "license": "Apache-2.0", "dependencies": { "@types/node": "^18.11.18", @@ -1189,9 +1260,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", - "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", "cpu": [ "arm" ], @@ -1203,9 +1274,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", - "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", "cpu": [ "arm64" ], @@ -1217,9 +1288,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", - "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", "cpu": [ "arm64" ], @@ -1231,9 +1302,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", - "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", "cpu": [ "x64" ], @@ -1245,9 +1316,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", - "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", "cpu": [ "arm64" ], @@ -1259,9 +1330,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", - "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", "cpu": [ "x64" ], @@ -1273,9 +1344,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", - "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", "cpu": [ "arm" ], @@ -1287,9 +1358,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", - "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", "cpu": [ "arm" ], @@ -1301,9 +1372,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", - "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", "cpu": [ "arm64" ], @@ -1315,9 +1386,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", - "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", "cpu": [ "arm64" ], @@ -1329,9 +1400,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", - "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", "cpu": [ "loong64" ], @@ -1343,9 +1414,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", - "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", "cpu": [ "ppc64" ], @@ -1357,9 +1428,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", - "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", "cpu": [ "riscv64" ], @@ -1371,9 +1442,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", - "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", "cpu": [ "riscv64" ], @@ -1385,9 +1456,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", - "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", "cpu": [ "s390x" ], @@ -1399,9 +1470,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", - "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", "cpu": [ "x64" ], @@ -1413,9 +1484,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", - "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", "cpu": [ "x64" ], @@ -1427,9 +1498,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", - "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", "cpu": [ "arm64" ], @@ -1441,9 +1512,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", - "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", "cpu": [ "arm64" ], @@ -1455,9 +1526,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", - "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", "cpu": [ "ia32" ], @@ -1469,9 +1540,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", - "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", "cpu": [ "x64" ], @@ -1483,9 +1554,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", - "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", "cpu": [ "x64" ], @@ -1632,6 +1703,8 @@ }, "node_modules/@types/node-fetch": { "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -1721,17 +1794,17 @@ } }, "node_modules/@vitest/expect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.8.tgz", - "integrity": "sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.13.tgz", + "integrity": "sha512-zYtcnNIBm6yS7Gpr7nFTmq8ncowlMdOJkWLqYvhr/zweY6tFbDkDi8BPPOeHxEtK1rSI69H7Fd4+1sqvEGli6w==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.0.8", - "@vitest/utils": "4.0.8", - "chai": "^6.2.0", + "@vitest/spy": "4.0.13", + "@vitest/utils": "4.0.13", + "chai": "^6.2.1", "tinyrainbow": "^3.0.3" }, "funding": { @@ -1739,13 +1812,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.8.tgz", - "integrity": "sha512-9FRM3MZCedXH3+pIh+ME5Up2NBBHDq0wqwhOKkN4VnvCiKbVxddqH9mSGPZeawjd12pCOGnl+lo/ZGHt0/dQSg==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.13.tgz", + "integrity": "sha512-eNCwzrI5djoauklwP1fuslHBjrbR8rqIVbvNlAnkq1OTa6XT+lX68mrtPirNM9TnR69XUPt4puBCx2Wexseylg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.0.8", + "@vitest/spy": "4.0.13", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1766,9 +1839,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.8.tgz", - "integrity": "sha512-qRrjdRkINi9DaZHAimV+8ia9Gq6LeGz2CgIEmMLz3sBDYV53EsnLZbJMR1q84z1HZCMsf7s0orDgZn7ScXsZKg==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.13.tgz", + "integrity": "sha512-ooqfze8URWbI2ozOeLDMh8YZxWDpGXoeY3VOgcDnsUxN0jPyPWSUvjPQWqDGCBks+opWlN1E4oP1UYl3C/2EQA==", "dev": true, "license": "MIT", "dependencies": { @@ -1779,13 +1852,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.8.tgz", - "integrity": "sha512-mdY8Sf1gsM8hKJUQfiPT3pn1n8RF4QBcJYFslgWh41JTfrK1cbqY8whpGCFzBl45LN028g0njLCYm0d7XxSaQQ==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.13.tgz", + "integrity": "sha512-9IKlAru58wcVaWy7hz6qWPb2QzJTKt+IOVKjAx5vb5rzEFPTL6H4/R9BMvjZ2ppkxKgTrFONEJFtzvnyEpiT+A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.8", + "@vitest/utils": "4.0.13", "pathe": "^2.0.3" }, "funding": { @@ -1793,13 +1866,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.8.tgz", - "integrity": "sha512-Nar9OTU03KGiubrIOFhcfHg8FYaRaNT+bh5VUlNz8stFhCZPNrJvmZkhsr1jtaYvuefYFwK2Hwrq026u4uPWCw==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.13.tgz", + "integrity": "sha512-hb7Usvyika1huG6G6l191qu1urNPsq1iFc2hmdzQY3F5/rTgqQnwwplyf8zoYHkpt7H6rw5UfIw6i/3qf9oSxQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.8", + "@vitest/pretty-format": "4.0.13", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1808,9 +1881,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.8.tgz", - "integrity": "sha512-nvGVqUunyCgZH7kmo+Ord4WgZ7lN0sOULYXUOYuHr55dvg9YvMz3izfB189Pgp28w0vWFbEEfNc/c3VTrqrXeA==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.13.tgz", + "integrity": "sha512-hSu+m4se0lDV5yVIcNWqjuncrmBgwaXa2utFLIrBkQCQkt+pSwyZTPFQAZiiF/63j8jYa8uAeUZ3RSfcdWaYWw==", "dev": true, "license": "MIT", "funding": { @@ -1818,13 +1891,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.8.tgz", - "integrity": "sha512-pdk2phO5NDvEFfUTxcTP8RFYjVj/kfLSPIN5ebP2Mu9kcIMeAQTbknqcFEyBcC4z2pJlJI9aS5UQjcYfhmKAow==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.13.tgz", + "integrity": "sha512-ydozWyQ4LZuu8rLp47xFUWis5VOKMdHjXCWhs1LuJsTNKww+pTHQNK4e0assIB9K80TxFyskENL6vCu3j34EYA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.8", + "@vitest/pretty-format": "4.0.13", "tinyrainbow": "^3.0.3" }, "funding": { @@ -1833,6 +1906,8 @@ }, "node_modules/abort-controller": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" @@ -1854,27 +1929,6 @@ "node": ">= 0.6" } }, - "node_modules/accepts/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" - } - }, - "node_modules/accepts/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -1886,6 +1940,8 @@ }, "node_modules/agentkeepalive": { "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", "license": "MIT", "dependencies": { "humanize-ms": "^1.2.1" @@ -1928,16 +1984,16 @@ } }, "node_modules/apify-client": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/apify-client/-/apify-client-2.19.0.tgz", - "integrity": "sha512-cDYwbygx/OplyF9MXTeb70nKwZHQQIp5OodsPeikWrh8sHmfeKWUu9jUUzeiWpHIPcwroYrP7KxA6UDSBGY3kQ==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/apify-client/-/apify-client-2.20.0.tgz", + "integrity": "sha512-oEMTImVVRZ5n8JkFV6dgbBFL3Xqz+GTwjUCjn/hwSNkow31Q8VNGk4qYDfRjkoqNQJ3ZirhtCwTnhkSXn1Tf+g==", "license": "Apache-2.0", "dependencies": { "@apify/consts": "^2.42.0", "@apify/log": "^2.2.6", - "@apify/utilities": "^2.18.0", + "@apify/utilities": "^2.23.2", "@crawlee/types": "^3.3.0", - "agentkeepalive": "^4.2.1", + "ansi-colors": "^4.1.1", "async-retry": "^1.3.3", "axios": "^1.6.7", "content-type": "^1.0.5", @@ -2009,6 +2065,8 @@ }, "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/axios": { @@ -2199,11 +2257,13 @@ } }, "node_modules/bullmq": { - "version": "5.63.0", + "version": "5.64.1", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.64.1.tgz", + "integrity": "sha512-Tg4ORit8bQ1xLwcQrEfcDpG50pS30Onuz1ZA4rPLbL9QEsOaBbvFQMSEvXSgMnvRZRqDggJoSBGz9tck+1PixQ==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", - "ioredis": "^5.4.1", + "ioredis": "^5.8.2", "msgpackr": "^1.11.2", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", @@ -2222,6 +2282,8 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2311,6 +2373,8 @@ }, "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" @@ -2336,6 +2400,8 @@ }, "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": { "delayed-stream": "~1.0.0" @@ -2351,15 +2417,16 @@ "license": "MIT" }, "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { @@ -2431,6 +2498,8 @@ }, "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" @@ -2460,6 +2529,8 @@ }, "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" @@ -2498,6 +2569,8 @@ }, "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": ">=0.4.0" @@ -2505,6 +2578,8 @@ }, "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" @@ -2521,6 +2596,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" @@ -2549,6 +2626,8 @@ }, "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", @@ -2624,6 +2703,8 @@ }, "node_modules/es-define-property": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2631,6 +2712,8 @@ }, "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" @@ -2645,6 +2728,8 @@ }, "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==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2655,6 +2740,8 @@ }, "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": { "es-errors": "^1.3.0", @@ -2796,6 +2883,8 @@ }, "node_modules/event-target-shim": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", "engines": { "node": ">=6" @@ -2862,27 +2951,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/express/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" - } - }, - "node_modules/express/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -3010,7 +3078,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -3025,10 +3095,35 @@ }, "node_modules/form-data-encoder": { "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", "license": "MIT" }, + "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", + "engines": { + "node": ">= 0.6" + } + }, + "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": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/formdata-node": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", "license": "MIT", "dependencies": { "node-domexception": "1.0.0", @@ -3093,6 +3188,8 @@ }, "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" @@ -3109,6 +3206,8 @@ }, "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", @@ -3131,6 +3230,8 @@ }, "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", @@ -3227,6 +3328,8 @@ }, "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" @@ -3249,6 +3352,8 @@ }, "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" @@ -3259,6 +3364,8 @@ }, "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": { "has-symbols": "^1.0.3" @@ -3272,6 +3379,8 @@ }, "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" @@ -3281,28 +3390,23 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "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", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-proxy-agent": { @@ -3333,6 +3437,8 @@ }, "node_modules/humanize-ms": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "license": "MIT", "dependencies": { "ms": "^2.0.0" @@ -3385,6 +3491,8 @@ }, "node_modules/ioredis": { "version": "5.8.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz", + "integrity": "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==", "license": "MIT", "dependencies": { "@ioredis/commands": "1.4.0", @@ -3517,6 +3625,8 @@ }, "node_modules/jose": { "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -3529,9 +3639,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -3593,10 +3703,14 @@ }, "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.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.isequal": { @@ -3623,6 +3737,8 @@ }, "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" @@ -3640,6 +3756,8 @@ }, "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" @@ -3681,20 +3799,28 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", + "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" } }, "node_modules/mime-types": { - "version": "2.1.35", + "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.52.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/minimatch": { @@ -3739,10 +3865,14 @@ }, "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/msgpackr": { "version": "1.11.5", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.5.tgz", + "integrity": "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==", "license": "MIT", "optionalDependencies": { "msgpackr-extract": "^3.0.2" @@ -3750,6 +3880,8 @@ }, "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, @@ -3825,10 +3957,15 @@ }, "node_modules/node-abort-controller": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", "license": "MIT" }, "node_modules/node-domexception": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -3846,6 +3983,8 @@ }, "node_modules/node-fetch": { "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -3864,6 +4003,8 @@ }, "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": { @@ -4211,9 +4352,9 @@ } }, "node_modules/puppeteer": { - "version": "24.30.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.30.0.tgz", - "integrity": "sha512-A5OtCi9WpiXBQgJ2vQiZHSyrAzQmO/WDsvghqlN4kgw21PhxA5knHUaUQq/N3EMt8CcvSS0RM+kmYLJmedR3TQ==", + "version": "24.31.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.31.0.tgz", + "integrity": "sha512-q8y5yLxLD8xdZdzNWqdOL43NbfvUOp60SYhaLZQwHC9CdKldxQKXOyJAciOr7oUJfyAH/KgB2wKvqT2sFKoVXA==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4221,7 +4362,7 @@ "chromium-bidi": "11.0.0", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1521046", - "puppeteer-core": "24.30.0", + "puppeteer-core": "24.31.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -4232,9 +4373,9 @@ } }, "node_modules/puppeteer-core": { - "version": "24.30.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.30.0.tgz", - "integrity": "sha512-2S3Smy0t0W4wJnNvDe7W0bE7wDmZjfZ3ljfMgJd6hn2Hq/f0jgN+x9PULZo2U3fu5UUIJ+JP8cNUGllu8P91Pg==", + "version": "24.31.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.31.0.tgz", + "integrity": "sha512-pnAohhSZipWQoFpXuGV7xCZfaGhqcBR9C4pVrU0QSrcMi7tQMH9J9lDBqBvyMAHQqe8HCARuREqFuVKRQOgTvg==", "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.10.13", @@ -4242,7 +4383,7 @@ "debug": "^4.4.3", "devtools-protocol": "0.0.1521046", "typed-query-selector": "^2.12.0", - "webdriver-bidi-protocol": "0.3.8", + "webdriver-bidi-protocol": "0.3.9", "ws": "^8.18.3" }, "engines": { @@ -4424,15 +4565,15 @@ } }, "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "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", - "http-errors": "2.0.0", - "iconv-lite": "0.7.0", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.10" @@ -4456,6 +4597,8 @@ }, "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" @@ -4472,6 +4615,8 @@ }, "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" @@ -4534,9 +4679,9 @@ } }, "node_modules/rollup": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", - "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", "dependencies": { @@ -4550,28 +4695,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.2", - "@rollup/rollup-android-arm64": "4.53.2", - "@rollup/rollup-darwin-arm64": "4.53.2", - "@rollup/rollup-darwin-x64": "4.53.2", - "@rollup/rollup-freebsd-arm64": "4.53.2", - "@rollup/rollup-freebsd-x64": "4.53.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", - "@rollup/rollup-linux-arm-musleabihf": "4.53.2", - "@rollup/rollup-linux-arm64-gnu": "4.53.2", - "@rollup/rollup-linux-arm64-musl": "4.53.2", - "@rollup/rollup-linux-loong64-gnu": "4.53.2", - "@rollup/rollup-linux-ppc64-gnu": "4.53.2", - "@rollup/rollup-linux-riscv64-gnu": "4.53.2", - "@rollup/rollup-linux-riscv64-musl": "4.53.2", - "@rollup/rollup-linux-s390x-gnu": "4.53.2", - "@rollup/rollup-linux-x64-gnu": "4.53.2", - "@rollup/rollup-linux-x64-musl": "4.53.2", - "@rollup/rollup-openharmony-arm64": "4.53.2", - "@rollup/rollup-win32-arm64-msvc": "4.53.2", - "@rollup/rollup-win32-ia32-msvc": "4.53.2", - "@rollup/rollup-win32-x64-gnu": "4.53.2", - "@rollup/rollup-win32-x64-msvc": "4.53.2", + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" } }, @@ -4591,26 +4736,6 @@ "node": ">= 18" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -4651,27 +4776,6 @@ "node": ">= 18" } }, - "node_modules/send/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" - } - }, - "node_modules/send/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", @@ -4945,6 +5049,8 @@ }, "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": { @@ -5086,10 +5192,14 @@ }, "node_modules/tr46": { "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, "node_modules/tslib": { "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/tsx": { @@ -5144,27 +5254,6 @@ "node": ">= 0.6" } }, - "node_modules/type-is/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" - } - }, - "node_modules/type-is/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/typed-query-selector": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", @@ -5173,6 +5262,8 @@ }, "node_modules/typescript": { "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", "peer": true, "bin": { @@ -5209,6 +5300,8 @@ }, "node_modules/uuid": { "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -5237,9 +5330,9 @@ } }, "node_modules/vite": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", - "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", + "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", "dev": true, "license": "MIT", "dependencies": { @@ -5312,19 +5405,19 @@ } }, "node_modules/vitest": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.8.tgz", - "integrity": "sha512-urzu3NCEV0Qa0Y2PwvBtRgmNtxhj5t5ULw7cuKhIHh3OrkKTLlut0lnBOv9qe5OvbkMH2g38G7KPDCTpIytBVg==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.13.tgz", + "integrity": "sha512-QSD4I0fN6uZQfftryIXuqvqgBxTvJ3ZNkF6RWECd82YGAYAfhcppBLFXzXJHQAAhVFyYEuFTrq6h0hQqjB7jIQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.0.8", - "@vitest/mocker": "4.0.8", - "@vitest/pretty-format": "4.0.8", - "@vitest/runner": "4.0.8", - "@vitest/snapshot": "4.0.8", - "@vitest/spy": "4.0.8", - "@vitest/utils": "4.0.8", + "@vitest/expect": "4.0.13", + "@vitest/mocker": "4.0.13", + "@vitest/pretty-format": "4.0.13", + "@vitest/runner": "4.0.13", + "@vitest/snapshot": "4.0.13", + "@vitest/spy": "4.0.13", + "@vitest/utils": "4.0.13", "debug": "^4.4.3", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", @@ -5350,12 +5443,13 @@ }, "peerDependencies": { "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", "@types/debug": "^4.1.12", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.0.8", - "@vitest/browser-preview": "4.0.8", - "@vitest/browser-webdriverio": "4.0.8", - "@vitest/ui": "4.0.8", + "@vitest/browser-playwright": "4.0.13", + "@vitest/browser-preview": "4.0.13", + "@vitest/browser-webdriverio": "4.0.13", + "@vitest/ui": "4.0.13", "happy-dom": "*", "jsdom": "*" }, @@ -5363,6 +5457,9 @@ "@edge-runtime/vm": { "optional": true }, + "@opentelemetry/api": { + "optional": true + }, "@types/debug": { "optional": true }, @@ -5391,23 +5488,29 @@ }, "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", "license": "MIT", "engines": { "node": ">= 14" } }, "node_modules/webdriver-bidi-protocol": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.8.tgz", - "integrity": "sha512-21Yi2GhGntMc671vNBCjiAeEVknXjVRoyu+k+9xOMShu+ZQfpGQwnBqbNz/Sv4GXZ6JmutlPAi2nIJcrymAWuQ==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.9.tgz", + "integrity": "sha512-uIYvlRQ0PwtZR1EzHlTMol1G0lAlmOe6wPykF9a77AK3bkpvZHzIVxRE2ThOx5vjy2zISe0zhwf5rzuUfbo1PQ==", "license": "Apache-2.0" }, "node_modules/webidl-conversions": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, "node_modules/whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -5415,9 +5518,9 @@ } }, "node_modules/which": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -5426,7 +5529,7 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/why-is-node-running": { diff --git a/services/worker/package.json b/services/worker/package.json index 153e9e1b..410d7582 100644 --- a/services/worker/package.json +++ b/services/worker/package.json @@ -6,7 +6,7 @@ "peerDependencies": { "typescript": "^5" }, - "version": "0.0.1", + "version": "0.2.0", "dependencies": { "@bull-board/express": "^6.14.0", "@mux/mux-node": "^12.8.0", @@ -15,7 +15,7 @@ "@types/node": "^24.10.1", "@types/semver": "^7.7.1", "@types/ssh2": "^1.15.5", - "apify-client": "^2.19.0", + "apify-client": "^2.20.0", "bullmq": "^5.63.0", "date-fns": "^4.1.0", "fs-extra": "^11.3.2", @@ -24,14 +24,14 @@ "nanoid": "^5.1.6", "onnxruntime-web": "^1.23.2", "pocketbase": "^0.26.3", - "puppeteer": "^24.30.0", + "puppeteer": "^24.31.0", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "semver": "^7.7.3", "sharp": "^0.34.5", "slugify": "^1.6.6", "ssh2": "^1.17.0", - "which": "^5.0.0" + "which": "^6.0.0" }, "devDependencies": { "tsx": "^4.20.6", @@ -40,4 +40,4 @@ "scripts": { "start": "tsx src/index.ts" } -} +} \ No newline at end of file diff --git a/services/worker/src/fixtures/sftp/data/.gitignore b/services/worker/src/fixtures/sftp/data/.gitignore new file mode 100644 index 00000000..a53d9628 --- /dev/null +++ b/services/worker/src/fixtures/sftp/data/.gitignore @@ -0,0 +1 @@ +*.mp4 \ No newline at end of file diff --git a/services/worker/src/fixtures/sftp/watch/.gitignore b/services/worker/src/fixtures/sftp/watch/.gitignore new file mode 100644 index 00000000..954eb7b9 --- /dev/null +++ b/services/worker/src/fixtures/sftp/watch/.gitignore @@ -0,0 +1 @@ +*.torrent \ No newline at end of file diff --git a/services/worker/src/index.ts b/services/worker/src/index.ts index c686f344..c54d9cce 100644 --- a/services/worker/src/index.ts +++ b/services/worker/src/index.ts @@ -10,6 +10,7 @@ import env from '../.config/env.ts'; import { version } from '../package.json'; import { downloadQueue } from './queues/downloadQueue.ts'; import { cacheQueue } from './queues/cacheQueue.ts'; +import { muxQueue } from './queues/muxQueue.ts'; const run = async () => { @@ -28,6 +29,7 @@ const run = async () => { new BullMQAdapter(gpuQueue), new BullMQAdapter(downloadQueue), new BullMQAdapter(cacheQueue), + new BullMQAdapter(muxQueue), ], serverAdapter, }); diff --git a/services/worker/src/processors/cacheCleanup.ts b/services/worker/src/processors/cacheCleanup.ts index e7d51006..020714d1 100644 --- a/services/worker/src/processors/cacheCleanup.ts +++ b/services/worker/src/processors/cacheCleanup.ts @@ -1,9 +1,9 @@ import { Job } from "bullmq"; import fs from "node:fs/promises"; -import path from "node:path"; +import { join } from "node:path"; import env from "../../.config/env"; -const retainmentDayCount = 7; +const retainmentDayCount = 90; /** * cacheCleanup @@ -11,7 +11,7 @@ const retainmentDayCount = 7; * Deletes files in the cache directory that are older than retainmentDayCount days */ export default async function cacheCleanup(job: Job) { - const cacheDir = env.CACHE_ROOT; + const cacheDir = join(env.CACHE_ROOT, 'worker'); let cleanedCount = 0; try { @@ -22,7 +22,7 @@ export default async function cacheCleanup(job: Job) { const retainMs = retainmentDayCount * 24 * 60 * 60 * 1000; // days → ms for (const file of files) { - const filePath = path.join(cacheDir, file); + const filePath = join(cacheDir, file); try { const stat = await fs.stat(filePath); // only delete files older than retainment diff --git a/services/worker/src/processors/cacheGet.ts b/services/worker/src/processors/cacheGet.ts index 1c55c51c..f508910a 100644 --- a/services/worker/src/processors/cacheGet.ts +++ b/services/worker/src/processors/cacheGet.ts @@ -18,7 +18,7 @@ interface FileInfo { fileId: string; } -const cacheRoot = env.CACHE_ROOT; +const cacheRoot = join(env?.CACHE_ROOT, 'worker'); function assertPayload(payload: any): asserts payload is Payload { if (typeof payload !== "object" || !payload) throw new Error("invalid payload-- was not an object."); @@ -53,13 +53,13 @@ export async function getB2FileInfo(job: Job, s3Key: string): Promise const args = ["file", "info", `b2://${env.AWS_BUCKET}/${s3Key}`]; let stdout: string; - await job.log(`Running ${cmd}, ${args.join(' ')}`); + await job.log(`Running ${cmd} ${args.join(' ')}`); try { const result = await spawn(cmd, args); stdout = result.stdout; } catch (err: any) { - throw new Error(`Failed to run 'b2 file info': ${err.stderr || err.message}`); + throw new Error(`Failed to run 'b2 file info': stderr:${err.stderr} message:${err.message}`); } let data: any; diff --git a/services/worker/src/processors/copyV1S3ToV2.ts b/services/worker/src/processors/copyV1S3ToV2.ts index 6daafdc0..c383f8e5 100644 --- a/services/worker/src/processors/copyV1S3ToV2.ts +++ b/services/worker/src/processors/copyV1S3ToV2.ts @@ -1,6 +1,3 @@ -// Copy futureporn.net s3 asset to future.porn s3 bucket. -// ex: https://futureporn-b2.b-cdn.net/(...) -> https://fp-usc.b-cdn.net/(...) - import logger from "../utils/logger"; import { Task } from "graphile-worker"; import { PrismaClient } from "../../generated/prisma"; @@ -81,8 +78,14 @@ async function copyFromBucketToBucket(spawn: typeof NanoSpawn, v1Url: string, v2 return v2Url; } -// example v1 https://futureporn-b2.b-cdn.net/projektmelody-chaturbate-2023-01-01.mp4 -// example v2 https://fp-usc.b-cdn.net/projektmelody-chaturbate-2023-01-01.mp4 +/** + * Copy futureporn.net s3 asset to future.porn s3 bucket. + * example: https://futureporn-b2.b-cdn.net/(...) -> https://fppbpro.b-cdn.net/(...) + * + * example of Futureporn v1 asset https://futureporn-b2.b-cdn.net/projektmelody-chaturbate-2023-01-01.mp4 + * example of Futureporn v2 asset https://fp-usc.b-cdn.net/projektmelody-chaturbate-2023-01-01.mp4 + * example of Futureporn v3 asset https://fppbpro.b-cdn.net/pbc_144770472/oj0lkw4it4f5mzx/projektmelody-fansly-2025-11-12.mp4 + */ const copyV1S3ToV2: Task = async (payload: any) => { logger.info(`copyV1S3ToV2 with vodId=${payload.vodId}`); diff --git a/services/worker/src/processors/copyV1VideoToV3.ts b/services/worker/src/processors/copyV1VideoToV3.ts index 7c6f5c5c..0bb62ad8 100644 --- a/services/worker/src/processors/copyV1VideoToV3.ts +++ b/services/worker/src/processors/copyV1VideoToV3.ts @@ -1,12 +1,10 @@ import { Job } from "bullmq"; import { getPocketBaseClient } from "../util/pocketbase"; import Client, { RecordModel } from "pocketbase"; -import { Vod } from "../types"; import { basename } from 'node:path'; import env from "../../.config/env"; import spawn from 'nano-spawn'; -const foo = 'bar'; /** * barFunction @@ -62,7 +60,7 @@ export async function copyV1VideoToV3(job: Job) { if (!vodId) throw new Error('vodId was missing from input data'); const pb = await getPocketBaseClient(); - const vod = await pb.collection('vods').getOne(job.data.vodId) + const vod = await pb.collection('vods').getOne(vodId) const sourceVideo = await copyBetweenBuckets(job, vod); diff --git a/services/worker/src/processors/createMuxAsset.ts b/services/worker/src/processors/createMuxAsset.ts index ace868b4..d2322520 100644 --- a/services/worker/src/processors/createMuxAsset.ts +++ b/services/worker/src/processors/createMuxAsset.ts @@ -2,7 +2,12 @@ import { Job } from "bullmq"; import { getPocketBaseClient } from "../util/pocketbase"; import Client, { RecordModel } from "pocketbase"; import { Vod } from "../types"; - +import { signUrl } from "../util/bunnyCDN"; +import env from "../../.config/env"; +import { basename } from "path"; +import { add, getUnixTime } from 'date-fns'; +import { generalQueue } from "../queues/generalQueue"; +import { highPriorityQueue } from "../queues/highPriorityQueue"; interface MuxAssetCreationResponse { data: { @@ -32,15 +37,22 @@ interface MuxAssetCreationResponse { */ export async function __createMuxAsset(vod: RecordModel) { - const { videoSrcB2, muxAssetId, muxPlaybackId } = vod; - if (!videoSrcB2) throw new Error(`vod ${vod.id} was missing videoSrcB2`); - + const { sourceVideo, muxAssetId, muxPlaybackId } = vod; + if (!sourceVideo) throw new Error(`vod ${vod.id} was missing sourceVideo`); if (muxAssetId !== '') throw new Error('this vod already has a muxAssetId'); if (muxPlaybackId !== '') throw new Error('this vod already has a muxPlaybackId'); + const expiry = getUnixTime(add(new Date(), { days: 30 })); + const b2Url = signUrl(env.BUNNY_TOKEN_KEY, env.BUNNY_ZONE_URL, sourceVideo, undefined, expiry); + const res = await fetch('https://api.mux.com/video/v1/assets', { + method: 'POST', + headers: { + 'Authorization': 'Basic ' + Buffer.from(`${env.MUX_TOKEN_ID}:${env.MUX_TOKEN_SECRET}`).toString('base64'), + 'Content-Type': 'application/json' + }, body: JSON.stringify({ - "input": videoSrcB2, + "input": b2Url, "playback_policy": [ "signed" ] @@ -81,6 +93,10 @@ export async function createMuxAsset(job: Job) { const { assetId, playbackId } = (await __createMuxAsset(vod)); job.log(`Created assetId=${assetId}, playbackId=${playbackId}`); pb.collection('vods').update(vodId, { muxAssetId: assetId, muxPlaybackId: playbackId }); + + // we need to sign the mux asset so it's playable on the webpage. + await highPriorityQueue.add('presignMuxAsset', { vodId }); + job.log('Vod record updated. All done.'); } diff --git a/services/worker/src/processors/createTorrent.ts b/services/worker/src/processors/createTorrent.ts index 56298580..fc595ad9 100644 --- a/services/worker/src/processors/createTorrent.ts +++ b/services/worker/src/processors/createTorrent.ts @@ -84,15 +84,29 @@ function assertPayload(payload: any): asserts payload is Payload { // } +/** + * + * Create a v1/v2 hybrid torrent using bittorrent. + * The default is to immediately delete the torrent from qBittorrent afterwards + * Because we seed from a seedbox instead. + * + * @param videoFilePath + * @param persist - whether or not to keep the torrent in qbittorrent after we have created it. + * @returns + */ async function createQBittorrentTorrent( - vodId: string, - videoFilePath: string + videoFilePath: string, + persist: boolean = false, ): Promise<{ magnetLink: string, torrentFilePath: string, info: QBTorrentInfo, }> { - return qbtClient.createTorrent(videoFilePath); + const torrent = await qbtClient.createTorrent(videoFilePath); + if (!persist) { + await qbtClient.deleteTorrent(torrent.info.hash); + } + return torrent; } // async function createTorrentfileTorrent( @@ -150,8 +164,14 @@ async function createQBittorrentTorrent( async function uploadTorrentToSeedbox(job: Job, videoFilePath: string, torrentFilePath: string) { job.log(`Uploading ${videoFilePath} to seedbox...`); + let lastLog = 0; // timestamp in ms + await sshClient.uploadFile(videoFilePath, './data', async ({ percent }) => { - await job.log(`Video upload progress: ${percent.toFixed(1)}%`); + const now = Date.now(); + if (now - lastLog >= 1_000) { // 10 seconds + lastLog = now; + await job.updateProgress(percent.toFixed(1)) + } }); job.log(`Uploading ${torrentFilePath} to seedbox...`); @@ -200,9 +220,13 @@ export async function createTorrent(job: Job) { await job.log(`cacheGet results: ${JSON.stringify(results)}`); const { cachePath } = results; - await job.log(`cachePath=${cachePath}. vodId=${vodId}. NEXT UP, create QBittorrentTorrent...`); + await job.log(`cachePath=${cachePath}. vodId=${vodId}.`); + await job.log(`Creating v1/v2 hybrid torrent using QBittorrentTorrent...`); + + // 4.5 Create a torrent using the local qBittorrent + const { magnetLink, torrentFilePath } = await createQBittorrentTorrent(cachePath); + - const { magnetLink, torrentFilePath } = await createQBittorrentTorrent(vodId, cachePath); await job.log(`great! torrent created at ${torrentFilePath}. Now let's upload that torrent and the VOD to the seedbox. This will take some time...`); await uploadTorrentToSeedbox(job, cachePath, torrentFilePath); @@ -211,7 +235,9 @@ export async function createTorrent(job: Job) { magnetLink }); - job.log(`🏆 torrent creation complete.`); + job.log(`Torrent creation complete.`); + await job.updateProgress(100); + return { magnetLink, cachePath, torrentFilePath, vodId }; diff --git a/services/worker/src/processors/download.ts b/services/worker/src/processors/download.ts index c4b73779..c556391b 100644 --- a/services/worker/src/processors/download.ts +++ b/services/worker/src/processors/download.ts @@ -49,8 +49,8 @@ async function monitorProgress(cachePath: string, expectedSize: number, job: Job try { const { size } = await stat(cachePath); - const progress = Math.min((size / expectedSize) * 100, 100); - await job.log(`size:${size}, expectedSize:${expectedSize}, progress:${progress}`); + const progress = Math.floor((size / expectedSize) * 100); + // await job.log(`size:${size}, expectedSize:${expectedSize}, progress:${progress}`); await job.updateProgress(progress); } catch { // file might not exist yet @@ -90,6 +90,7 @@ export async function __download(job: Job, s3Key: string, cachePath: string) { stopMonitor(); // always stop monitor, even on error } + await job.updateProgress(100); job.log('Download complete.'); } diff --git a/services/worker/src/processors/findWork.ts b/services/worker/src/processors/findWork.ts index 547b1c0d..54316cb0 100644 --- a/services/worker/src/processors/findWork.ts +++ b/services/worker/src/processors/findWork.ts @@ -1,41 +1,156 @@ -import { Job } from "bullmq"; +import { Job, Queue } from "bullmq"; import { getPocketBaseClient } from "../util/pocketbase"; import Client from "pocketbase"; import { generalQueue } from "../queues/generalQueue"; +import { muxQueue } from "../queues/muxQueue"; +const queues: Record = { + generalQueue: generalQueue, + muxQueue: muxQueue, +}; -export async function findMissingTorrent(job: Job, pb: Client) { +type VodJobConfig = { + filter: string; + queueName: string; + processorName: string; + logMessage: (vodId: string) => string; +}; +async function handleMissing(job: Job, pb: Client, config: VodJobConfig) { const results = await pb.collection('vods').getList(1, 1, { - filter: "videoSrcB2 != '' && magnetLink = ''", - sort: '-streamDate' + filter: config.filter, + sort: '-created', }); + const vods = results.items; + if (!vods.length) return; // nothing to do + const vod = vods[0]; + const vodId = vod.id; - job.log(`findWork found ${vod.id} in need of a torrent.`); + job.log(config.logMessage(vodId)); - const jobId = `createTorrent-${vod.id}`; + const jobId = `${config.processorName}-${vodId}`; - const existingJob = await generalQueue.getJob(jobId); + const queue = queues[config.queueName]; // <-- look here + await queue.add(config.processorName, { vodId }, { jobId }); +} - // only add a createTorrent job if there isn't one queued - if (existingJob?.isActive) { - job.log(`refusing to add createTorrent job for vod ${vod.id} because a similar job is active`); - } else { +// export async function handleMissingTorrent(job: Job, pb: Client) { - await generalQueue.add('createTorrent', { - vodId: vod.id - }, { - jobId - }); - } +// const results = await pb.collection('vods').getList(1, 1, { +// filter: "sourceVideo != '' && magnetLink = ''", +// sort: '-created' +// }); +// const vods = results.items; +// const vod = vods[0]; + +// job.log(`findWork found ${vod.id} in need of a torrent.`); + +// const jobId = `createTorrent-${vod.id}`; + +// await generalQueue.add('createTorrent', { +// vodId: vod.id +// }, { +// jobId +// }); +// } + +// export async function handleMissingStreamDate(job: Job, pb: Client) { +// const results = await pb.collection('vods').getList(1, 1, { +// filter: "announceUrl != '' && streamDate = ''", +// sort: '-created' +// }); +// const vods = results.items; +// if (vods.length === 0) return; // no vods with missing streamDate. + +// const vod = vods[0]; +// const vodId = vod.id; +// job.log(`findWork found ${vodId} in need of a stream date.`); +// const jobId = `getAnnounceUrlDetails-${vodId}`; + +// await generalQueue.add('getAnnounceUrlDetails', { +// vodId: vodId +// }, { +// jobId +// }); +// } + +// export async function handleMissingSourceVideo(job: Job, pb: Client) { +// const results = await pb.collection('vods').getList(1, 1, { +// filter: "videoSrcB2 != '' && sourceVideo = ''", +// sort: '-created', +// }); +// const vods = results.items; +// if (vods.length === 0) return; // no vods with missing sourceVideo +// const vod = vods[0]; +// const vodId = vod.id; +// job.log(`findWork found ${vodId} in need of a source video.`); +// const jobId = `handleMissingSourceVideo-${vodId}`; +// await generalQueue.add('copyV1VideoToV3', { +// vodId +// }, { +// jobId +// }); +// } + +// export async function handleMissingMuxAsset(job: Job, pb: Client) { +// const results = await pb.collection('vods').getList(1, 1, { +// filter: "muxAssetId = '' && muxPlaybackId = '' && sourceVideo != ''", +// sort: '-created', +// }); +// // ... +// } + + + +export async function handleMissingTorrent(job: Job, pb: Client) { + return handleMissing(job, pb, { + filter: "sourceVideo != '' && magnetLink = ''", + queueName: 'generalQueue', + processorName: 'createTorrent', + logMessage: (id) => `findWork found ${id} in need of a torrent.` + }); +} + +export async function handleMissingStreamDate(job: Job, pb: Client) { + return handleMissing(job, pb, { + filter: "announceUrl != '' && streamDate = ''", + queueName: 'generalQueue', + processorName: 'getAnnounceUrlDetails', + logMessage: (id) => `findWork found ${id} in need of a stream date.` + }); +} + +export async function handleMissingSourceVideo(job: Job, pb: Client) { + return handleMissing(job, pb, { + filter: "videoSrcB2 != '' && sourceVideo = ''", + queueName: 'generalQueue', + processorName: 'copyV1VideoToV3', + logMessage: (id) => `findWork found ${id} in need of a source video.` + }); +} + +/** + * handleMissingMuxAsset + * + * We only add Mux assets to new vods because Mux cost a lot of money. + * Future plan is to build our own Mux alternative and remove Mux. + */ +export async function handleMissingMuxAsset(job: Job, pb: Client) { + const sinceDate = '2025-11-01'; + return handleMissing(job, pb, { + filter: `created > '${sinceDate}' && muxAssetId = '' && muxPlaybackId = '' && sourceVideo != ''`, + queueName: 'muxQueue', // whatever queue name you use + processorName: 'createMuxAsset', + logMessage: (id) => `findWork found ${id} in need of a Mux asset.` + }); } /** * - * findWork + * This processor is all about identifying known issues in the database and handling them by delegating the work to a processor. * * Remember to makes processors * * idempotent @@ -46,7 +161,11 @@ export async function findWork(job: Job) { const pb = await getPocketBaseClient(); - await findMissingTorrent(job, pb); + await handleMissingTorrent(job, pb); + await handleMissingStreamDate(job, pb); + await handleMissingSourceVideo(job, pb); + await handleMissingMuxAsset(job, pb); + // findMissingThumbnail // findMissingAudioAnalysis diff --git a/services/worker/src/processors/getAnnounceUrlDetails.ts b/services/worker/src/processors/getAnnounceUrlDetails.ts index 291cb20e..18fa3757 100644 --- a/services/worker/src/processors/getAnnounceUrlDetails.ts +++ b/services/worker/src/processors/getAnnounceUrlDetails.ts @@ -32,11 +32,12 @@ export function getTweetDates(tweetUrls: string[]): Date[] { } export async function getVodsWithAnnounceUrlAndNoStreamDate() { - const pb = await getPocketBaseClient() + const pb = await getPocketBaseClient(); const results = await pb.collection('vods').getList(1, 25, { - filter: "announceUrl != '' && streamDate = ''" - }) + filter: "announceUrl != '' && streamDate = ''", + requestKey: `getAnnounceUrlDetails-1..25` + }); const vods = results.items; return vods; diff --git a/services/worker/src/processors/presignMuxAssets.ts b/services/worker/src/processors/presignMuxAssets.ts index 0ac82595..629460b7 100644 --- a/services/worker/src/processors/presignMuxAssets.ts +++ b/services/worker/src/processors/presignMuxAssets.ts @@ -1,6 +1,6 @@ import Mux from '@mux/mux-node'; import env from '../../.config/env'; -import { type QueueOptions, type Job } from 'bullmq'; +import { type Job } from 'bullmq'; import { getPocketBaseClient } from '../util/pocketbase'; @@ -25,8 +25,21 @@ async function createToken(playbackId: string) { return token } +export async function presignMuxAsset(job: Job) { + const pb = await getPocketBaseClient(); + const vodId = job.data.vodId as string; + const vod = await pb.collection('vods').getOne(vodId); -export async function presignMuxAssets(job: Job) { + const muxPlaybackId = vod?.muxPlaybackId; + if (!muxPlaybackId) throw new Error(`presignMuxAsset called with vodId ${vodId} failed because this vod is missing a muxPlaybackId.`); + const muxPlaybackToken = await createToken(muxPlaybackId); + await pb.collection('vods').update(vod.id, { + muxPlaybackToken + }); +} + + +export async function presignAllMuxAssets(job: Job) { const pb = await getPocketBaseClient(); diff --git a/services/worker/src/queues/generalQueue.ts b/services/worker/src/queues/generalQueue.ts index d1b1c3a0..00b79a68 100644 --- a/services/worker/src/queues/generalQueue.ts +++ b/services/worker/src/queues/generalQueue.ts @@ -8,14 +8,14 @@ await generalQueue.upsertJobScheduler( pattern: '3 7 * * *', // Runs at 07:03 every day }, { - name: 'presignMuxAsset', + name: 'presignAllMuxAssets', data: {}, opts: {}, // Optional additional job options }, ); -// await generalQueue.upsertJobScheduler( -// 'find-work-every-one-minute', -// { every: 1000 * 60 }, -// { name: 'findWork' } -// ) \ No newline at end of file +await generalQueue.upsertJobScheduler( + 'find-work-often', + { every: 1000 * 30 }, + { name: 'findWork' } +) \ No newline at end of file diff --git a/services/worker/src/queues/gpuQueue.ts b/services/worker/src/queues/gpuQueue.ts index d229af46..2cdd7ae7 100644 --- a/services/worker/src/queues/gpuQueue.ts +++ b/services/worker/src/queues/gpuQueue.ts @@ -3,15 +3,3 @@ import { Queue } from "bullmq"; import { connection } from "../../.config/bullmq.config"; export const gpuQueue = new Queue('gpuQueue', { connection }); -await gpuQueue.upsertJobScheduler( - 'schedule-vod-processing-recurring', - { - pattern: '* * * * *' - }, - { - name: 'cron-schedule-vod-processing', - data: {}, - opts: {} - }, -) - diff --git a/services/worker/src/queues/highPriorityQueue.ts b/services/worker/src/queues/highPriorityQueue.ts index dd23bc17..e7c22e94 100644 --- a/services/worker/src/queues/highPriorityQueue.ts +++ b/services/worker/src/queues/highPriorityQueue.ts @@ -13,15 +13,3 @@ await highPriorityQueue.upsertJobScheduler( opts: {} }, ) - -await highPriorityQueue.upsertJobScheduler( - 'get-announce-url-details', - { - every: 1000 * 63 - }, - { - name: 'getAnnounceUrlDetails', - data: {}, - opts: {}, - }, -); \ No newline at end of file diff --git a/services/worker/src/queues/muxQueue.ts b/services/worker/src/queues/muxQueue.ts new file mode 100644 index 00000000..4d98f089 --- /dev/null +++ b/services/worker/src/queues/muxQueue.ts @@ -0,0 +1,6 @@ +import { Queue, QueueEvents } from 'bullmq'; +import { connection } from '../../.config/bullmq.config'; +export const muxQueue = new Queue('muxQueue', { connection }); +export const muxQueueEvents = new QueueEvents("muxQueue", { + connection +}); \ No newline at end of file diff --git a/services/worker/src/util/b2.README.md b/services/worker/src/util/b2.README.md new file mode 100644 index 00000000..c815f24f --- /dev/null +++ b/services/worker/src/util/b2.README.md @@ -0,0 +1,101 @@ +## Known errors + +### Error: Failed to run 'b2 file info': b2: tar_extract_all() failed: Invalid argument + +This one is weird. It happens when several failed b2cli are running in the background. I came across this during testing and first I thought my executable was somehow corrupted. + +``` +cj@cj54hd:~/Downloads$ b2 file info b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 +b2: tar_extract_all() failed: Invalid argument +cj@cj54hd:~/Downloads$ b2 file ls b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 +b2: tar_extract_all() failed: Invalid argument +cj@cj54hd:~/Downloads$ b2 ls b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 +b2: tar_extract_all() failed: Invalid argument +cj@cj54hd:~/Downloads$ b2 ls +b2: tar_extract_all() failed: Invalid argument +cj@cj54hd:~/Downloads$ b2 -h +b2: tar_extract_all() failed: Invalid argument +cj@cj54hd:~/Downloads$ b2 +b2: tar_extract_all() failed: Invalid argument +cj@cj54hd:~/Downloads$ which b2 +/home/cj/.local/bin/b2 +cj@cj54hd:~/Downloads$ head b2 +head: cannot open 'b2' for reading: No such file or directory +cj@cj54hd:~/Downloads$ ~/.local/bin/b2 +b2: tar_extract_all() failed: Invalid argument +cj@cj54hd:~/Downloads$ ls -la ~/.local/bin/b2 +-rwxrwxr-x 1 cj cj 32597952 Aug 23 17:07 /home/cj/.local/bin/b2 +cj@cj54hd:~/Downloads$ file ~/.local/bin/b2 +/home/cj/.local/bin/b2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped +cj@cj54hd:~/Downloads$ cp b2v3-linux ~/.local/bin/b2 +cp: cannot create regular file '/home/cj/.local/bin/b2': Text file busy + +``` + +apparently there were a shitton of b2 instances running in the background! + +``` +cj@cj54hd:~/Downloads$ ps aux | grep 'b2:/[/]' +cj 191334 0.0 0.0 292 148 pts/11 S+ Nov21 0:00 b2 file download b2://fppbdev/pbc_144770472/6v80rae2m128yw1/projektmelody-chaturbate-2025-10-19.mp4 /tmp/vods/6v80rae2m128yw1/sourceVideo/13629592283.mp4 +cj 191335 0.0 0.0 2840 2004 pts/11 S+ Nov21 0:00 /tmp/staticx-gDLAjK/b2v3 file download b2://fppbdev/pbc_144770472/6v80rae2m128yw1/projektmelody-chaturbate-2025-10-19.mp4 /tmp/vods/6v80rae2m128yw1/sourceVideo/13629592283.mp4 +cj 191336 0.2 0.3 950960 90496 pts/11 Sl+ Nov21 5:48 /tmp/staticx-gDLAjK/b2v3 file download b2://fppbdev/pbc_144770472/6v80rae2m128yw1/projektmelody-chaturbate-2025-10-19.mp4 /tmp/vods/6v80rae2m128yw1/sourceVideo/13629592283.mp4 +cj 205138 0.0 0.0 292 152 pts/11 S+ Nov21 0:00 b2 file download b2://fppbdev/pbc_144770472/6v80rae2m128yw1/projektmelody-chaturbate-2025-10-19.mp4 /tmp/vods/6v80rae2m128yw1/sourceVideo/13629592283.mp4 +cj 205139 0.0 0.0 2840 1988 pts/11 S+ Nov21 0:00 /tmp/staticx-OMOMPi/b2v3 file download b2://fppbdev/pbc_144770472/6v80rae2m128yw1/projektmelody-chaturbate-2025-10-19.mp4 /tmp/vods/6v80rae2m128yw1/sourceVideo/13629592283.mp4 +cj 205140 0.0 0.3 950960 90528 pts/11 Sl+ Nov21 0:18 /tmp/staticx-OMOMPi/b2v3 file download b2://fppbdev/pbc_144770472/6v80rae2m128yw1/projektmelody-chaturbate-2025-10-19.mp4 /tmp/vods/6v80rae2m128yw1/sourceVideo/13629592283.mp4 +cj 257781 0.0 0.0 292 96 pts/11 S+ Nov21 0:00 b2 file download b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 /tmp/2024-03-10-el_xox-4csbaj06u481x9t.mp4 +cj 257782 0.0 0.0 2840 2028 pts/11 S+ Nov21 0:00 /tmp/staticx-GFFmLI/b2v3 file download b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 /tmp/2024-03-10-el_xox-4csbaj06u481x9t.mp4 +cj 257783 0.0 0.3 951984 90080 pts/11 Sl+ Nov21 1:37 /tmp/staticx-GFFmLI/b2v3 file download b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 /tmp/2024-03-10-el_xox-4csbaj06u481x9t.mp4 +cj 264819 0.0 0.0 292 148 pts/11 S+ Nov21 0:00 b2 file download b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 /tmp/vods/4csbaj06u481x9t/sourceVideo/3835045469/2024-03-10-el_xox-4csbaj06u481x9t.mp4 +cj 264820 0.0 0.0 2840 2032 pts/11 S+ Nov21 0:00 /tmp/staticx-NFonPF/b2v3 file download b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 /tmp/vods/4csbaj06u481x9t/sourceVideo/3835045469/2024-03-10-el_xox-4csbaj06u481x9t.mp4 +cj 264821 0.1 0.3 950956 90488 pts/11 Sl+ Nov21 2:47 /tmp/staticx-NFonPF/b2v3 file download b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 /tmp/vods/4csbaj06u481x9t/sourceVideo/3835045469/2024-03-10-el_xox-4csbaj06u481x9t.mp4 +cj 607425 0.0 0.0 292 148 pts/11 S+ Nov22 0:00 b2 file download b2://fppbdev/pbc_144770472/2ct5pnh3fdx91ks/projektmelody-chaturbate-2024-03-04.mp4 /tmp/vods/2ct5pnh3fdx91ks/sourceVideo/4784598000/2024-03-03-projektmelody-2ct5pnh3fdx91ks.mp4 +cj 607426 0.0 0.0 2840 1972 pts/11 S+ Nov22 0:00 /tmp/staticx-cjcddG/b2v3 file download b2://fppbdev/pbc_144770472/2ct5pnh3fdx91ks/projektmelody-chaturbate-2024-03-04.mp4 /tmp/vods/2ct5pnh3fdx91ks/sourceVideo/4784598000/2024-03-03-projektmelody-2ct5pnh3fdx91ks.mp4 +cj 607427 0.1 0.3 950956 90492 pts/11 Sl+ Nov22 1:09 /tmp/staticx-cjcddG/b2v3 file download b2://fppbdev/pbc_144770472/2ct5pnh3fdx91ks/projektmelody-chaturbate-2024-03-04.mp4 /tmp/vods/2ct5pnh3fdx91ks/sourceVideo/4784598000/2024-03-03-projektmelody-2ct5pnh3fdx91ks.mp4 +``` + +kill 'em! + +``` +cj@cj54hd:~/Downloads$ killall b2 +cj@cj54hd:~/Downloads$ ps aux | grep 'b2:/[/]' +cj@cj54hd:~/Downloads$ +``` + +and now it works. holy shit! + +``` +cj@cj54hd:~/Downloads$ b2 file info b2://fppbdev/pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4 +{ + "cacheControl": "max-age=604800", + "contentSha1": "84bbd4218b16184f6204eeb8094720150c8479d5", + "contentType": "video/mp4", + "fileId": "4_zad63b74f11035a9993a30b18_f100aec8c206f88f4_d20251116_m225322_c000_v0001417_t0037_u01763333602168", + "fileInfo": { + "large_file_sha1": "84bbd4218b16184f6204eeb8094720150c8479d5", + "src_last_modified_millis": "1710056557459" + }, + "fileName": "pbc_144770472/4csbaj06u481x9t/el_xox-chaturbate-2024-03-10.mp4", + "fileRetention": { + "mode": null, + "retainUntilTimestamp": null + }, + "legalHold": null, + "replicationStatus": null, + "serverSideEncryption": { + "mode": "none" + }, + "size": 3835045469, + "uploadTimestamp": 1763333602168 +} +``` + +But it still failed when run by BullMQ. + +``` +Error: Failed to run 'b2 file info': b2: tar_extract_all() failed: Invalid argument + at onFailed (/home/cj/Documents/futureporn-monorepo/services/worker/node_modules/bullmq/src/classes/job.ts:1295:16) + at (/home/cj/Documents/futureporn-monorepo/services/worker/node_modules/bullmq/src/classes/job.ts:1324:11) + at process.processTicksAndRejections (node:internal/process/task_queues:105:5) +``` + +OH, it is actually that my /tmp dir doesn't have enough space to store the file being downloaded! @see https://github.com/JonathonReinhart/staticx/issues/244 \ No newline at end of file diff --git a/services/worker/src/util/bunnyCDN.spec.ts b/services/worker/src/util/bunnyCDN.spec.ts new file mode 100644 index 00000000..69f0eb7f --- /dev/null +++ b/services/worker/src/util/bunnyCDN.spec.ts @@ -0,0 +1,30 @@ +import { describe, it, expect } from "vitest"; +import { signUrl } from "./bunnyCDN.ts"; + +describe("signUrl", () => { + it("generates a correct signed BunnyCDN URL", () => { + const securityKey = "my-secret"; + const baseUrl = "https://cdn.example.com"; + const path = "/videos/test.mp4"; + const rawQuery = "width=500&quality=80"; + const expires = 1732600000; + + const signed = signUrl(securityKey, baseUrl, path, rawQuery, expires); + + // token part is deterministic but long, so let's just check it contains required parts + expect(signed).toContain(baseUrl + path); + expect(signed).toContain("token="); + expect(signed).toContain("quality=80&width=500"); + expect(signed).toContain(`expires=${expires}`); + }); + + it("throws if baseUrl ends with slash", () => { + expect(() => signUrl("k", "https://example.com/", "/file.jpg", "", 123)) + .toThrow(/must not end with a slash/); + }); + + it("prepends slash if path does not start with one", () => { + const out = signUrl("k", "https://x", "file.jpg", "", 1); + expect(out.startsWith("https://x/file.jpg")).toBe(true); + }); +}); diff --git a/services/worker/src/util/bunnyCDN.ts b/services/worker/src/util/bunnyCDN.ts new file mode 100644 index 00000000..cad26fce --- /dev/null +++ b/services/worker/src/util/bunnyCDN.ts @@ -0,0 +1,49 @@ + +import crypto from "node:crypto"; + + +/** + * Generate a signed BunnyCDN URL. + * + * @see https://support.bunny.net/hc/en-us/articles/360016055099-How-to-sign-URLs-for-BunnyCDN-Token-Authentication + */ +export function signUrl(securityKey: string, baseUrl: string, path: string, rawQuery = "", expires: number) { + if (!path) throw new Error('signUrl requires a path argument, but it was falsy.'); + if (!path.startsWith('/')) path = '/' + path; + if (baseUrl.endsWith('/')) throw new Error(`baseUrl must not end with a slash. got baseUrl=${baseUrl}`); + + // Build parameter string (sort keys alphabetically) + let parameterData = ""; + if (rawQuery) { + const params = rawQuery + .split("&") + .map(p => p.split("=")) + .filter(([key]) => key && key !== "token" && key !== "expires") + .sort(([a], [b]) => a.localeCompare(b)); + + if (params.length) { + parameterData = params.map(([k, v]) => `${k}=${v}`).join("&"); + } + } + + // Build hashable base + const hashableBase = securityKey + path + expires + parameterData; + // console.log(`hashableBase`, hashableBase) + + // Compute token using your $security.sha256 workflow + + const tokenH = crypto.createHash("sha256").update(hashableBase).digest("hex"); + const token = Buffer.from(tokenH, "hex") + .toString("base64") + .replace(/\n/g, "") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); + + // Build final signed URL + let tokenUrl = baseUrl + path + "?token=" + token; + if (parameterData) tokenUrl += "&" + parameterData; + tokenUrl += "&expires=" + expires; + + return tokenUrl; +} \ No newline at end of file diff --git a/services/worker/src/util/qbittorrent.ts b/services/worker/src/util/qbittorrent.ts index 057640c5..7d8f5166 100644 --- a/services/worker/src/util/qbittorrent.ts +++ b/services/worker/src/util/qbittorrent.ts @@ -383,6 +383,28 @@ export class QBittorrentClient { return filePath; } + + /** + * get torrent info from qBittorrent + * When a torrent is created, It can take some time before it appears in the list. So we retry it. + * + * @param torrentName + * @returns + */ + async getTorrentInfos(torrentName: string): Promise { + return retry( + () => this.__getTorrentInfos(torrentName), + 6, + 500 + ); + } + + /** + * Generally it's preferred to use this.getTorrentInfos over this.__getTorrentInfos because it is more fault tolerant. + * + * @param torrentName + * @returns + */ private async __getTorrentInfos(torrentName: string): Promise { if (!torrentName) throw new Error('__getTorrentInfos requires torrentName as first arg. However, arg was falsy. '); // ensure we're logged in @@ -412,12 +434,8 @@ export class QBittorrentClient { async getInfoHashV2(torrentName: string): Promise { console.log(`getInfoHashV2 using torrentName=${torrentName}`) - // __getTorrentInfos can take some time. So we retry it every 1/2 second up to 6 times - const torrent = await retry( - () => this.__getTorrentInfos(torrentName), - 6, - 500 - ); + + const torrent = await this.getTorrentInfos(torrentName); return torrent.infohash_v2; } @@ -430,7 +448,7 @@ export class QBittorrentClient { const bn = basename(localFilePath).replace('.torrent', ''); await this.connect(); await this.__addTorrent(localFilePath); - return (await this.__getTorrentInfos(bn)); + return (await this.getTorrentInfos(bn)); } /** @@ -480,6 +498,7 @@ export class QBittorrentClient { */ async deleteTorrent(id: string): Promise { await this.connect(); + console.log(`Deleting torrent ${id}...`); if (!this.sidCookie) { throw new Error('Not logged in. sidCookie missing.'); @@ -495,7 +514,7 @@ export class QBittorrentClient { hashToDelete = id; } else { // Not a hash → treat as name → look up hash - const info = await this.__getTorrentInfos(id); + const info = await this.getTorrentInfos(id); console.log('info', info); hashToDelete = info.hash; } @@ -537,14 +556,14 @@ export class QBittorrentClient { /** * - * @deprecated use __getTorrentInfos instead + * @deprecated use getTorrentInfos instead */ async getMagnetLink(fileName: string): Promise { console.log(`getMagnetLink using fileName=${fileName}`) // qBittorrent does NOT return infoHash directly here // we have to get it by querying the torrents list - const torrent = await this.__getTorrentInfos(fileName); + const torrent = await this.getTorrentInfos(fileName); if (!torrent) { throw new Error(`Torrent ${fileName} not found in qBittorrent after adding`); @@ -579,7 +598,7 @@ export class QBittorrentClient { // 5. Get magnet link console.log('lets get the torrent infos'); - const info = await this.__getTorrentInfos(basename(localFilePath)) + const info = await this.getTorrentInfos(basename(localFilePath)) const magnetLink = info.magnet_uri; return { diff --git a/services/worker/src/workers/generalWorker.ts b/services/worker/src/workers/generalWorker.ts index a9631b46..13aee5a9 100644 --- a/services/worker/src/workers/generalWorker.ts +++ b/services/worker/src/workers/generalWorker.ts @@ -2,18 +2,23 @@ import { Worker } from 'bullmq'; import { connection } from '../../.config/bullmq.config.ts'; -import { presignMuxAssets } from '../processors/presignMuxAssets.ts'; +import { presignAllMuxAssets, presignMuxAsset } from '../processors/presignMuxAssets.ts'; import { copyV1VideoAll } from '../processors/copyV1VideoAll.ts'; import { copyV2ThumbToV3 } from '../processors/copyV2ThumbToV3.ts'; import { copyV1VideoToV3 } from '../processors/copyV1VideoToV3.ts'; import { createTorrent } from '../processors/createTorrent.ts'; import { analyzeAudio } from '../processors/analyzeAudio.ts'; import { findWork } from '../processors/findWork.ts'; +import { getAnnounceUrlDetails } from '../processors/getAnnounceUrlDetails.ts'; + new Worker( 'generalQueue', async (job) => { console.log('generalWorker. we got a job on the generalQueue.', job.data, job.name); switch (job.name) { + case 'getAnnounceUrlDetails': + return await getAnnounceUrlDetails(job); + case 'findWork': return await findWork(job); @@ -23,8 +28,11 @@ new Worker( case 'copyV1VideoToV3': return await copyV1VideoToV3(job); - case 'presignMuxAssets': - return await presignMuxAssets(job); + case 'presignAllMuxAssets': + return await presignAllMuxAssets(job); + + case 'presignMuxAsset': + return await presignMuxAsset(job); case 'copyV2ThumbToV3': return await copyV2ThumbToV3(job); diff --git a/services/worker/src/workers/highPriorityWorker.ts b/services/worker/src/workers/highPriorityWorker.ts index b64c8fb1..2c016b57 100644 --- a/services/worker/src/workers/highPriorityWorker.ts +++ b/services/worker/src/workers/highPriorityWorker.ts @@ -15,13 +15,17 @@ new Worker( case '': throw new Error('missing job name.') case 'syncronizePatreon': - return await syncronizePatreon(job); + await syncronizePatreon(job); + break; case 'getAnnounceUrlDetails': - return await getAnnounceUrlDetails(job); + await getAnnounceUrlDetails(job); + break; case 'createTorrent': - return await createTorrent(job); + await createTorrent(job); + break; case 'copyV1VideoToV3': - return await copyV1VideoToV3(job); + await copyV1VideoToV3(job); + break; default: throw new Error(`Unknown job name: ${job.name}`); } diff --git a/services/worker/src/workers/muxWorker.ts b/services/worker/src/workers/muxWorker.ts new file mode 100644 index 00000000..4abb03e3 --- /dev/null +++ b/services/worker/src/workers/muxWorker.ts @@ -0,0 +1,23 @@ +import { Worker } from 'bullmq'; +import { connection } from '../../.config/bullmq.config.ts'; +import { createMuxAsset } from '../processors/createMuxAsset.ts'; + +const workerName = 'muxWorker'; +const queueName = 'muxQueue'; + +new Worker( + queueName, + async (job) => { + console.log(`${workerName}. we got a job on the ${queueName}. data=${JSON.stringify(job.data)}, job name=${job.name}`); + switch (job.name) { + case 'createMuxAsset': + return await createMuxAsset(job); + + default: + throw new Error(`${workerName} Unknown job name: ${job.name}`); + } + }, + { connection } +); + +console.log(`${workerName} is running...`);