A simple tool which lets you scrape twitter accounts and crosspost them to bluesky accounts! Comes with a CLI and a webapp for managing profiles! Works with images/videos/link embeds/threads.
11
fork

Configure Feed

Select the types of activity you want to include in your feed.

Migrate runtime workflows to Bun and harden profile sync behavior

j4ckxyz 53e0be0c 08f3e975

+2800 -257
+10 -8
Dockerfile
··· 1 1 # syntax=docker/dockerfile:1.7 2 2 3 - FROM node:22-bookworm-slim AS build 3 + FROM oven/bun:1-slim AS build 4 4 5 5 WORKDIR /app 6 6 ··· 12 12 ca-certificates \ 13 13 && rm -rf /var/lib/apt/lists/* 14 14 15 - COPY package.json package-lock.json ./ 15 + COPY package.json ./ 16 + COPY bun.lock ./bun.lock 16 17 COPY scripts ./scripts 17 18 18 - RUN npm ci 19 + RUN bun install --frozen-lockfile 19 20 20 21 COPY . . 21 22 22 - RUN npm run build \ 23 - && npm prune --omit=dev 23 + RUN bun run build \ 24 + && bun install --frozen-lockfile --production 24 25 25 26 26 - FROM node:22-bookworm-slim AS runtime 27 + FROM oven/bun:1-slim AS runtime 27 28 28 29 WORKDIR /app 29 30 ··· 41 42 && rm -rf /var/lib/apt/lists/* 42 43 43 44 COPY --from=build /app/package.json ./package.json 45 + COPY --from=build /app/bun.lock ./bun.lock 44 46 COPY --from=build /app/node_modules ./node_modules 45 47 COPY --from=build /app/dist ./dist 46 48 COPY --from=build /app/web/dist ./web/dist ··· 53 55 54 56 EXPOSE 3000 55 57 56 - HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=5 CMD ["node", "-e", "fetch('http://127.0.0.1:' + (process.env.PORT || 3000) + '/api/auth/bootstrap-status').then((res) => process.exit(res.ok ? 0 : 1)).catch(() => process.exit(1))"] 58 + HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=5 CMD ["bun", "-e", "fetch('http://127.0.0.1:' + (process.env.PORT || 3000) + '/api/auth/bootstrap-status').then((res) => process.exit(res.ok ? 0 : 1)).catch(() => process.exit(1))"] 57 59 58 60 ENTRYPOINT ["/usr/bin/tini", "--"] 59 - CMD ["node", "dist/index.js"] 61 + CMD ["bun", "dist/index.js"]
+51 -49
README.md
··· 42 42 43 43 ```bash 44 44 docker logs -f tweets-2-bsky 45 - docker exec -it tweets-2-bsky node dist/cli.js status 45 + docker exec -it tweets-2-bsky bun dist/cli.js status 46 46 docker stop tweets-2-bsky 47 47 docker start tweets-2-bsky 48 48 ``` ··· 83 83 84 84 What this does by default: 85 85 86 + - auto-installs Bun (latest stable for your platform) when missing 87 + - auto-upgrades Bun to latest stable before install/build 86 88 - installs dependencies 87 89 - builds server + web dashboard 88 90 - creates/updates `.env` with sensible defaults (`PORT=3000`, generated `JWT_SECRET` if missing) ··· 190 192 You can run CLI commands without leaving Docker: 191 193 192 194 ```bash 193 - docker exec -it tweets-2-bsky node dist/cli.js status 194 - docker exec -it tweets-2-bsky node dist/cli.js run-now 195 - docker exec -it tweets-2-bsky node dist/cli.js list 195 + docker exec -it tweets-2-bsky bun dist/cli.js status 196 + docker exec -it tweets-2-bsky bun dist/cli.js run-now 197 + docker exec -it tweets-2-bsky bun dist/cli.js list 196 198 ``` 197 199 198 200 ### 6) Updating Docker deployments ··· 338 340 339 341 If running from source instead of Docker: 340 342 341 - - Node.js 22+ 342 - - npm 343 + - Bun 1.x+ (auto-installed/upgraded by `install.sh` and `update.sh`) 343 344 - git 344 345 345 346 Optional but recommended for source installs: ··· 355 356 ```bash 356 357 git clone https://github.com/j4ckxyz/tweets-2-bsky 357 358 cd tweets-2-bsky 358 - npm install 359 - npm run build 360 - npm start 359 + bun install 360 + bun run build 361 + bun run start 361 362 ``` 362 363 363 364 Open: [http://localhost:3000](http://localhost:3000) ··· 376 377 EOF 377 378 ``` 378 379 379 - ### Rebuild native modules after Node version changes 380 + ### Rebuild native modules 380 381 381 382 ```bash 382 - npm run rebuild:native 383 - npm run build 383 + bun run rebuild:native 384 + bun run build 384 385 ``` 385 386 386 387 ## First-Time Setup via CLI (Alternative to Web Forms) 387 388 388 389 ```bash 389 - npm run cli -- setup-twitter 390 - npm run cli -- add-mapping 391 - npm run cli -- run-now 390 + bun run cli -- setup-twitter 391 + bun run cli -- add-mapping 392 + bun run cli -- run-now 392 393 ``` 393 394 394 395 `add-mapping` now runs a guided onboarding flow: ··· 403 404 Always invoke CLI commands as: 404 405 405 406 ```bash 406 - npm run cli -- <command> 407 + bun run cli -- <command> 407 408 ``` 408 409 409 410 ### Status and basic operations 410 411 411 412 ```bash 412 - npm run cli -- status 413 - npm run cli -- list 414 - npm run cli -- recent-activity --limit 20 413 + bun run cli -- status 414 + bun run cli -- list 415 + bun run cli -- recent-activity --limit 20 415 416 ``` 416 417 417 418 ### Credentials and configuration 418 419 419 420 ```bash 420 - npm run cli -- setup-twitter 421 - npm run cli -- setup-ai 422 - npm run cli -- set-interval 5 421 + bun run cli -- setup-twitter 422 + bun run cli -- setup-ai 423 + bun run cli -- set-interval 5 423 424 ``` 424 425 425 426 ### Mapping management 426 427 427 428 ```bash 428 - npm run cli -- add-mapping 429 - npm run cli -- sync-profile <mapping-id-or-handle> --source <twitter-username> 430 - npm run cli -- edit-mapping <mapping-id-or-handle> 431 - npm run cli -- remove <mapping-id-or-handle> 429 + bun run cli -- add-mapping 430 + bun run cli -- sync-profile <mapping-id-or-handle> --source <twitter-username> 431 + bun run cli -- edit-mapping <mapping-id-or-handle> 432 + bun run cli -- remove <mapping-id-or-handle> 432 433 ``` 433 434 434 435 ### Running syncs 435 436 436 437 ```bash 437 - npm run cli -- run-now 438 - npm run cli -- run-now --dry-run 439 - npm run cli -- run-now --web 438 + bun run cli -- run-now 439 + bun run cli -- run-now --dry-run 440 + bun run cli -- run-now --web 440 441 ``` 441 442 442 443 ### Backfill and history import 443 444 444 445 ```bash 445 - npm run cli -- backfill <mapping-id-or-handle> --limit 50 446 - npm run cli -- import-history <mapping-id-or-handle> --limit 100 447 - npm run cli -- clear-cache <mapping-id-or-handle> 446 + bun run cli -- backfill <mapping-id-or-handle> --limit 50 447 + bun run cli -- import-history <mapping-id-or-handle> --limit 100 448 + bun run cli -- clear-cache <mapping-id-or-handle> 448 449 ``` 449 450 450 451 ### Dangerous operation (admin workflow) 451 452 452 453 ```bash 453 - npm run cli -- delete-all-posts <mapping-id-or-handle> 454 + bun run cli -- delete-all-posts <mapping-id-or-handle> 454 455 ``` 455 456 456 457 ### Config export/import 457 458 458 459 ```bash 459 - npm run cli -- config-export ./tweets-2-bsky-config.json 460 - npm run cli -- config-import ./tweets-2-bsky-config.json 460 + bun run cli -- config-export ./tweets-2-bsky-config.json 461 + bun run cli -- config-import ./tweets-2-bsky-config.json 461 462 ``` 462 463 463 464 Mapping references accept: ··· 471 472 Run every 5 minutes: 472 473 473 474 ```cron 474 - */5 * * * * cd /path/to/tweets-2-bsky && /usr/bin/npm run cli -- run-now >> /tmp/tweets-2-bsky.log 2>&1 475 + */5 * * * * cd /path/to/tweets-2-bsky && /usr/local/bin/bun run cli -- run-now >> /tmp/tweets-2-bsky.log 2>&1 475 476 ``` 476 477 477 478 Run one backfill once: 478 479 479 480 ```bash 480 - npm run cli -- backfill <mapping-id-or-handle> --limit 50 481 + bun run cli -- backfill <mapping-id-or-handle> --limit 50 481 482 ``` 482 483 483 484 ## Background Runtime Options ··· 493 494 ### Option B: manage PM2 directly 494 495 495 496 ```bash 496 - pm2 start dist/index.js --name tweets-2-bsky 497 + pm2 start dist/index.js --name tweets-2-bsky --interpreter bun 497 498 pm2 logs tweets-2-bsky 498 499 pm2 restart tweets-2-bsky --update-env 499 500 pm2 save ··· 503 504 504 505 ```bash 505 506 mkdir -p data/runtime 506 - nohup npm start > data/runtime/tweets-2-bsky.log 2>&1 & 507 + nohup bun run start > data/runtime/tweets-2-bsky.log 2>&1 & 507 508 echo $! > data/runtime/tweets-2-bsky.pid 508 509 ``` 509 510 ··· 525 526 526 527 - stashes local uncommitted changes before pull and restores them after update 527 528 - pulls latest code (supports non-`origin` remotes and detached-head recovery) 529 + - ensures Bun is installed and upgraded to latest stable 528 530 - installs dependencies 529 - - rebuilds native modules when Node ABI changed 531 + - rebuilds native modules when runtime/dependencies changed 530 532 - builds server + web dashboard 531 533 - restarts existing runtime for PM2 **or** nohup mode 532 534 - preserves local `config.json` and `.env` with backup/restore ··· 584 586 ### Start backend/scheduler from source 585 587 586 588 ```bash 587 - npm run dev 589 + bun run dev 588 590 ``` 589 591 590 592 ### Start Vite web dev server 591 593 592 594 ```bash 593 - npm run dev:web 595 + bun run dev:web 594 596 ``` 595 597 596 598 ### Build and quality checks 597 599 598 600 ```bash 599 - npm run build 600 - npm run typecheck 601 - npm run lint 601 + bun run build 602 + bun run typecheck 603 + bun run lint 602 604 ``` 603 605 604 606 ## Troubleshooting 605 607 606 608 See: `TROUBLESHOOTING.md` 607 609 608 - Common recovery after changing Node versions: 610 + Common recovery when native modules fail to load: 609 611 610 612 ```bash 611 - npm run rebuild:native 612 - npm run build 613 - npm start 613 + bun run rebuild:native 614 + bun run build 615 + bun run start 614 616 ``` 615 617 616 618 ## License
+23 -13
TROUBLESHOOTING.md
··· 16 16 ./update.sh 17 17 ``` 18 18 19 - ### PM2 "MODULE_NOT_FOUND" Error 20 - If you see errors about `npm` not being found in the logs after an update: 19 + ### PM2 interpreter mismatch 20 + If PM2 logs show command/runtime errors after an update (for example old `npm`/`node` interpreter paths): 21 21 22 22 1. Run the repair script: 23 23 ```bash 24 24 chmod +x repair_pm2.sh 25 - ./repair_pm2.sh 25 + ./repair_pm2.sh 26 + ``` 27 + 28 + ### `bun: command not found` 29 + If Bun is missing on a source install host: 30 + 31 + 1. Run either installer/updater once (they auto-install and auto-upgrade Bun to latest stable): 32 + ```bash 33 + ./install.sh --no-start 34 + # or 35 + ./update.sh --no-restart 26 36 ``` 27 37 28 - ### `better-sqlite3` NODE_MODULE_VERSION mismatch 29 - If startup fails with `ERR_DLOPEN_FAILED` and a `NODE_MODULE_VERSION` mismatch: 38 + ### Native module load failure (`ERR_DLOPEN_FAILED`) 39 + If startup fails while loading native dependencies: 30 40 31 - 1. Rebuild native bindings for your active Node version: 41 + 1. Reinstall/rebuild native dependencies with Bun: 32 42 ```bash 33 - npm run rebuild:native 43 + bun run rebuild:native 34 44 ``` 35 45 2. Rebuild and start: 36 46 ```bash 37 - npm run build 38 - npm start 47 + bun run build 48 + bun run start 39 49 ``` 40 50 41 51 ### Dashboard appears unstyled / plain text UI ··· 43 53 44 54 1. Rebuild web assets: 45 55 ```bash 46 - npm run build 56 + bun run build 47 57 ``` 48 58 2. Restart server: 49 59 ```bash 50 - npm start 60 + bun run start 51 61 ``` 52 62 3. Hard refresh browser cache (`Cmd+Shift+R` on macOS). 53 63 54 64 ### CLI command not recognized 55 - When using npm scripts, pass CLI args after `--`: 65 + When using Bun scripts, pass CLI args after `--`: 56 66 57 67 ```bash 58 - npm run cli -- status 68 + bun run cli -- status 59 69 ``` 60 70 61 71 ### Docker: permissions writing `/app/data`
+1151
bun.lock
··· 1 + { 2 + "lockfileVersion": 1, 3 + "configVersion": 0, 4 + "workspaces": { 5 + "": { 6 + "name": "tweets-2-bsky", 7 + "dependencies": { 8 + "@atproto/api": "^0.18.9", 9 + "@google/generative-ai": "^0.24.1", 10 + "@the-convocation/twitter-scraper": "^0.21.1", 11 + "axios": "^1.13.2", 12 + "bcryptjs": "^3.0.3", 13 + "better-sqlite3": "^12.5.0", 14 + "cheerio": "^1.1.2", 15 + "class-variance-authority": "^0.7.1", 16 + "clsx": "^2.1.1", 17 + "commander": "^14.0.2", 18 + "cors": "^2.8.5", 19 + "dotenv": "^17.2.3", 20 + "express": "^5.2.1", 21 + "franc-min": "^6.2.0", 22 + "inquirer": "^13.1.0", 23 + "iso-639-1": "^3.1.2", 24 + "jsonwebtoken": "^9.0.3", 25 + "lucide-react": "^0.553.0", 26 + "node-cron": "^4.2.1", 27 + "puppeteer-core": "^24.34.0", 28 + "react": "^19.2.0", 29 + "react-dom": "^19.2.0", 30 + "sharp": "^0.34.5", 31 + "tailwind-merge": "^3.3.1", 32 + }, 33 + "devDependencies": { 34 + "@biomejs/biome": "^1.9.4", 35 + "@types/bcryptjs": "^2.4.6", 36 + "@types/better-sqlite3": "^7.6.13", 37 + "@types/cheerio": "^0.22.35", 38 + "@types/cors": "^2.8.19", 39 + "@types/express": "^5.0.6", 40 + "@types/inquirer": "^9.0.9", 41 + "@types/jsonwebtoken": "^9.0.10", 42 + "@types/node": "^22.10.2", 43 + "@types/react": "^19.2.6", 44 + "@types/react-dom": "^19.2.3", 45 + "@types/sharp": "^0.31.1", 46 + "@vitejs/plugin-react": "^5.1.0", 47 + "autoprefixer": "^10.4.21", 48 + "postcss": "^8.5.6", 49 + "tailwindcss": "^3.4.18", 50 + "tsx": "^4.19.2", 51 + "typescript": "^5.7.2", 52 + "vite": "^7.1.12", 53 + }, 54 + }, 55 + }, 56 + "packages": { 57 + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], 58 + 59 + "@atproto/api": ["@atproto/api@0.18.21", "", { "dependencies": { "@atproto/common-web": "^0.4.16", "@atproto/lexicon": "^0.6.1", "@atproto/syntax": "^0.4.3", "@atproto/xrpc": "^0.7.7", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", "zod": "^3.23.8" } }, "sha512-s35MIJerGT/pKe2xJtKKswqlIr/ola2r2iURBKBL0Mk1OKe6jP4YvTMh1N2d2PEANFzNNTbKoDaLfJPo2Uvc/w=="], 60 + 61 + "@atproto/common-web": ["@atproto/common-web@0.4.16", "", { "dependencies": { "@atproto/lex-data": "^0.0.11", "@atproto/lex-json": "^0.0.11", "@atproto/syntax": "^0.4.3", "zod": "^3.23.8" } }, "sha512-Ufvaff5JgxUyUyTAG0/3o7ltpy3lnZ1DvLjyAnvAf+hHfiK7OMQg+8byr+orN+KP9MtIQaRTsCgYPX+PxMKUoA=="], 62 + 63 + "@atproto/lex-data": ["@atproto/lex-data@0.0.11", "", { "dependencies": { "multiformats": "^9.9.0", "tslib": "^2.8.1", "uint8arrays": "3.0.0", "unicode-segmenter": "^0.14.0" } }, "sha512-4+KTtHdqwlhiTKA7D4SACea4jprsNpCQsNALW09wsZ6IHhCDGO5tr1cmV+QnLYe3G3mu1E1yXHXbPUHrUUDT/A=="], 64 + 65 + "@atproto/lex-json": ["@atproto/lex-json@0.0.11", "", { "dependencies": { "@atproto/lex-data": "^0.0.11", "tslib": "^2.8.1" } }, "sha512-2IExAoQ4KsR5fyPa1JjIvtR316PvdgRH/l3BVGLBd3cSxM3m5MftIv1B6qZ9HjNiK60SgkWp0mi9574bTNDhBQ=="], 66 + 67 + "@atproto/lexicon": ["@atproto/lexicon@0.6.1", "", { "dependencies": { "@atproto/common-web": "^0.4.13", "@atproto/syntax": "^0.4.3", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw=="], 68 + 69 + "@atproto/syntax": ["@atproto/syntax@0.4.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA=="], 70 + 71 + "@atproto/xrpc": ["@atproto/xrpc@0.7.7", "", { "dependencies": { "@atproto/lexicon": "^0.6.0", "zod": "^3.23.8" } }, "sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA=="], 72 + 73 + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], 74 + 75 + "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], 76 + 77 + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], 78 + 79 + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], 80 + 81 + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], 82 + 83 + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], 84 + 85 + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], 86 + 87 + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], 88 + 89 + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], 90 + 91 + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], 92 + 93 + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], 94 + 95 + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], 96 + 97 + "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], 98 + 99 + "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], 100 + 101 + "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], 102 + 103 + "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], 104 + 105 + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], 106 + 107 + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], 108 + 109 + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], 110 + 111 + "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], 112 + 113 + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], 114 + 115 + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], 116 + 117 + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], 118 + 119 + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], 120 + 121 + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], 122 + 123 + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], 124 + 125 + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], 126 + 127 + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], 128 + 129 + "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], 130 + 131 + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], 132 + 133 + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], 134 + 135 + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], 136 + 137 + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], 138 + 139 + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], 140 + 141 + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], 142 + 143 + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], 144 + 145 + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], 146 + 147 + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], 148 + 149 + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], 150 + 151 + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], 152 + 153 + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], 154 + 155 + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], 156 + 157 + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], 158 + 159 + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], 160 + 161 + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], 162 + 163 + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], 164 + 165 + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], 166 + 167 + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], 168 + 169 + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], 170 + 171 + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], 172 + 173 + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], 174 + 175 + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], 176 + 177 + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], 178 + 179 + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], 180 + 181 + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], 182 + 183 + "@google/generative-ai": ["@google/generative-ai@0.24.1", "", {}, "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q=="], 184 + 185 + "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], 186 + 187 + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], 188 + 189 + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], 190 + 191 + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], 192 + 193 + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], 194 + 195 + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], 196 + 197 + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], 198 + 199 + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], 200 + 201 + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], 202 + 203 + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], 204 + 205 + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], 206 + 207 + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], 208 + 209 + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], 210 + 211 + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], 212 + 213 + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], 214 + 215 + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], 216 + 217 + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], 218 + 219 + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], 220 + 221 + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], 222 + 223 + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], 224 + 225 + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], 226 + 227 + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], 228 + 229 + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], 230 + 231 + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], 232 + 233 + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], 234 + 235 + "@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="], 236 + 237 + "@inquirer/checkbox": ["@inquirer/checkbox@5.0.4", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-DrAMU3YBGMUAp6ArwTIp/25CNDtDbxk7UjIrrtM25JVVrlVYlVzHh5HR1BDFu9JMyUoZ4ZanzeaHqNDttf3gVg=="], 238 + 239 + "@inquirer/confirm": ["@inquirer/confirm@6.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-WdaPe7foUnoGYvXzH4jp4wH/3l+dBhZ3uwhKjXjwdrq5tEIFaANxj6zrGHxLdsIA0yKM0kFPVcEalOZXBB5ISA=="], 240 + 241 + "@inquirer/core": ["@inquirer/core@11.1.1", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^9.0.2" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-hV9o15UxX46OyQAtaoMqAOxGR8RVl1aZtDx1jHbCtSJy1tBdTfKxLPKf7utsE4cRy4tcmCQ4+vdV+ca+oNxqNA=="], 242 + 243 + "@inquirer/editor": ["@inquirer/editor@5.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/external-editor": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-QI3Jfqcv6UO2/VJaEFONH8Im1ll++Xn/AJTBn9Xf+qx2M+H8KZAdQ5sAe2vtYlo+mLW+d7JaMJB4qWtK4BG3pw=="], 244 + 245 + "@inquirer/expand": ["@inquirer/expand@5.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-0I/16YwPPP0Co7a5MsomlZLpch48NzYfToyqYAOWtBmaXSB80RiNQ1J+0xx2eG+Wfxt0nHtpEWSRr6CzNVnOGg=="], 246 + 247 + "@inquirer/external-editor": ["@inquirer/external-editor@2.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.2" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w=="], 248 + 249 + "@inquirer/figures": ["@inquirer/figures@2.0.3", "", {}, "sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g=="], 250 + 251 + "@inquirer/input": ["@inquirer/input@5.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-4B3s3jvTREDFvXWit92Yc6jF1RJMDy2VpSqKtm4We2oVU65YOh2szY5/G14h4fHlyQdpUmazU5MPCFZPRJ0AOw=="], 252 + 253 + "@inquirer/number": ["@inquirer/number@4.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-CmMp9LF5HwE+G/xWsC333TlCzYYbXMkcADkKzcawh49fg2a1ryLc7JL1NJYYt1lJ+8f4slikNjJM9TEL/AljYQ=="], 254 + 255 + "@inquirer/password": ["@inquirer/password@5.0.4", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-ZCEPyVYvHK4W4p2Gy6sTp9nqsdHQCfiPXIP9LbJVW4yCinnxL/dDDmPaEZVysGrj8vxVReRnpfS2fOeODe9zjg=="], 256 + 257 + "@inquirer/prompts": ["@inquirer/prompts@8.2.0", "", { "dependencies": { "@inquirer/checkbox": "^5.0.4", "@inquirer/confirm": "^6.0.4", "@inquirer/editor": "^5.0.4", "@inquirer/expand": "^5.0.4", "@inquirer/input": "^5.0.4", "@inquirer/number": "^4.0.4", "@inquirer/password": "^5.0.4", "@inquirer/rawlist": "^5.2.0", "@inquirer/search": "^4.1.0", "@inquirer/select": "^5.0.4" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-rqTzOprAj55a27jctS3vhvDDJzYXsr33WXTjODgVOru21NvBo9yIgLIAf7SBdSV0WERVly3dR6TWyp7ZHkvKFA=="], 258 + 259 + "@inquirer/rawlist": ["@inquirer/rawlist@5.2.0", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-CciqGoOUMrFo6HxvOtU5uL8fkjCmzyeB6fG7O1vdVAZVSopUBYECOwevDBlqNLyyYmzpm2Gsn/7nLrpruy9RFg=="], 260 + 261 + "@inquirer/search": ["@inquirer/search@4.1.0", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-EAzemfiP4IFvIuWnrHpgZs9lAhWDA0GM3l9F4t4mTQ22IFtzfrk8xbkMLcAN7gmVML9O/i+Hzu8yOUyAaL6BKA=="], 262 + 263 + "@inquirer/select": ["@inquirer/select@5.0.4", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-s8KoGpPYMEQ6WXc0dT9blX2NtIulMdLOO3LA1UKOiv7KFWzlJ6eLkEYTDBIi+JkyKXyn8t/CD6TinxGjyLt57g=="], 264 + 265 + "@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" } }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="], 266 + 267 + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 268 + 269 + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], 270 + 271 + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 272 + 273 + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 274 + 275 + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 276 + 277 + "@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="], 278 + 279 + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 280 + 281 + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], 282 + 283 + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], 284 + 285 + "@puppeteer/browsers": ["@puppeteer/browsers@2.12.1", "", { "dependencies": { "debug": "^4.4.3", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.4", "tar-fs": "^3.1.1", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-fXa6uXLxfslBlus3MEpW8S6S9fe5RwmAE5Gd8u3krqOwnkZJV3/lQJiY3LaFdTctLLqJtyMgEUGkbDnRNf6vbQ=="], 286 + 287 + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], 288 + 289 + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], 290 + 291 + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], 292 + 293 + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="], 294 + 295 + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="], 296 + 297 + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="], 298 + 299 + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="], 300 + 301 + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="], 302 + 303 + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="], 304 + 305 + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="], 306 + 307 + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="], 308 + 309 + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="], 310 + 311 + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="], 312 + 313 + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="], 314 + 315 + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="], 316 + 317 + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="], 318 + 319 + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="], 320 + 321 + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="], 322 + 323 + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="], 324 + 325 + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="], 326 + 327 + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="], 328 + 329 + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="], 330 + 331 + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="], 332 + 333 + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="], 334 + 335 + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="], 336 + 337 + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], 338 + 339 + "@sinclair/typebox": ["@sinclair/typebox@0.32.35", "", {}, "sha512-Ul3YyOTU++to8cgNkttakC0dWvpERr6RYoHO2W47DLbFvrwBDJUY31B1sImH6JZSYc4Kt4PyHtoPNu+vL2r2dA=="], 340 + 341 + "@the-convocation/twitter-scraper": ["@the-convocation/twitter-scraper@0.21.1", "", { "dependencies": { "@sinclair/typebox": "^0.32.20", "cross-fetch": "^4.0.0-alpha.5", "debug": "^4.4.1", "headers-polyfill": "^3.1.2", "json-stable-stringify": "^1.0.2", "linkedom": "^0.18.12", "otpauth": "^9.2.2", "set-cookie-parser": "^2.6.0", "tough-cookie": "^4.1.2", "tslib": "^2.5.2", "x-client-transaction-id": "^0.1.9" }, "peerDependencies": { "cycletls": "^2.0.5" }, "optionalPeers": ["cycletls"] }, "sha512-q8HjTWQyrNMEitJli4/cG1ht0sV67bwWQKv4jWhQnq1OuFNBepdU/kCsbnf43UUk6BgmHdmH3uvHeIjGowqrVQ=="], 342 + 343 + "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="], 344 + 345 + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], 346 + 347 + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], 348 + 349 + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], 350 + 351 + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], 352 + 353 + "@types/bcryptjs": ["@types/bcryptjs@2.4.6", "", {}, "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ=="], 354 + 355 + "@types/better-sqlite3": ["@types/better-sqlite3@7.6.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA=="], 356 + 357 + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], 358 + 359 + "@types/cheerio": ["@types/cheerio@0.22.35", "", { "dependencies": { "@types/node": "*" } }, "sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA=="], 360 + 361 + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], 362 + 363 + "@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="], 364 + 365 + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 366 + 367 + "@types/express": ["@types/express@5.0.6", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "^2" } }, "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA=="], 368 + 369 + "@types/express-serve-static-core": ["@types/express-serve-static-core@5.1.1", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A=="], 370 + 371 + "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], 372 + 373 + "@types/inquirer": ["@types/inquirer@9.0.9", "", { "dependencies": { "@types/through": "*", "rxjs": "^7.2.0" } }, "sha512-/mWx5136gts2Z2e5izdoRCo46lPp5TMs9R15GTSsgg/XnZyxDWVqoVU3R9lWnccKpqwsJLvRoxbCjoJtZB7DSw=="], 374 + 375 + "@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="], 376 + 377 + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], 378 + 379 + "@types/node": ["@types/node@22.19.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w=="], 380 + 381 + "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], 382 + 383 + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], 384 + 385 + "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], 386 + 387 + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], 388 + 389 + "@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="], 390 + 391 + "@types/serve-static": ["@types/serve-static@2.2.0", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*" } }, "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ=="], 392 + 393 + "@types/sharp": ["@types/sharp@0.31.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag=="], 394 + 395 + "@types/through": ["@types/through@0.0.33", "", { "dependencies": { "@types/node": "*" } }, "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ=="], 396 + 397 + "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], 398 + 399 + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.4", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA=="], 400 + 401 + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], 402 + 403 + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], 404 + 405 + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], 406 + 407 + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], 408 + 409 + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], 410 + 411 + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], 412 + 413 + "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], 414 + 415 + "ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="], 416 + 417 + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], 418 + 419 + "autoprefixer": ["autoprefixer@10.4.24", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001766", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": "bin/autoprefixer" }, "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw=="], 420 + 421 + "await-lock": ["await-lock@2.2.2", "", {}, "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw=="], 422 + 423 + "axios": ["axios@1.13.5", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q=="], 424 + 425 + "b4a": ["b4a@1.7.4", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-u20zJLDaSWpxaZ+zaAkEIB2dZZ1o+DF4T/MRbmsvGp9nletHOyiai19OzX1fF8xUBYsO1bPXxODvcd0978pnug=="], 426 + 427 + "bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="], 428 + 429 + "bare-fs": ["bare-fs@4.5.4", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-POK4oplfA7P7gqvetNmCs4CNtm9fNsx+IAh7jH7GgU0OJdge2rso0R20TNWVq6VoWcCvsTdlNDaleLHGaKx8CA=="], 430 + 431 + "bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="], 432 + 433 + "bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="], 434 + 435 + "bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "^2.21.0" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="], 436 + 437 + "bare-url": ["bare-url@2.3.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw=="], 438 + 439 + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], 440 + 441 + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": "dist/cli.js" }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], 442 + 443 + "basic-ftp": ["basic-ftp@5.1.0", "", {}, "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw=="], 444 + 445 + "bcryptjs": ["bcryptjs@3.0.3", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g=="], 446 + 447 + "better-sqlite3": ["better-sqlite3@12.6.2", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA=="], 448 + 449 + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], 450 + 451 + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], 452 + 453 + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], 454 + 455 + "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], 456 + 457 + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], 458 + 459 + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], 460 + 461 + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": "cli.js" }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], 462 + 463 + "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], 464 + 465 + "buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], 466 + 467 + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], 468 + 469 + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], 470 + 471 + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], 472 + 473 + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], 474 + 475 + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], 476 + 477 + "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], 478 + 479 + "caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="], 480 + 481 + "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], 482 + 483 + "cheerio": ["cheerio@1.2.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="], 484 + 485 + "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], 486 + 487 + "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], 488 + 489 + "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], 490 + 491 + "chromium-bidi": ["chromium-bidi@14.0.0", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw=="], 492 + 493 + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], 494 + 495 + "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], 496 + 497 + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], 498 + 499 + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 500 + 501 + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], 502 + 503 + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 504 + 505 + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 506 + 507 + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], 508 + 509 + "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], 510 + 511 + "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], 512 + 513 + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], 514 + 515 + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], 516 + 517 + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], 518 + 519 + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], 520 + 521 + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], 522 + 523 + "cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="], 524 + 525 + "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], 526 + 527 + "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], 528 + 529 + "cssesc": ["cssesc@3.0.0", "", { "bin": "bin/cssesc" }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], 530 + 531 + "cssom": ["cssom@0.5.0", "", {}, "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="], 532 + 533 + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], 534 + 535 + "data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="], 536 + 537 + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], 538 + 539 + "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], 540 + 541 + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], 542 + 543 + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], 544 + 545 + "degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="], 546 + 547 + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], 548 + 549 + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], 550 + 551 + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 552 + 553 + "devtools-protocol": ["devtools-protocol@0.0.1566079", "", {}, "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ=="], 554 + 555 + "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], 556 + 557 + "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], 558 + 559 + "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], 560 + 561 + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], 562 + 563 + "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], 564 + 565 + "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], 566 + 567 + "dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], 568 + 569 + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], 570 + 571 + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], 572 + 573 + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], 574 + 575 + "electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="], 576 + 577 + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], 578 + 579 + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], 580 + 581 + "encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="], 582 + 583 + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], 584 + 585 + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], 586 + 587 + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], 588 + 589 + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], 590 + 591 + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], 592 + 593 + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], 594 + 595 + "esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": "bin/esbuild" }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], 596 + 597 + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], 598 + 599 + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], 600 + 601 + "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "escodegen": "bin/escodegen.js", "esgenerate": "bin/esgenerate.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="], 602 + 603 + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], 604 + 605 + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], 606 + 607 + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], 608 + 609 + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], 610 + 611 + "events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="], 612 + 613 + "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], 614 + 615 + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], 616 + 617 + "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": "cli.js" }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], 618 + 619 + "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], 620 + 621 + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], 622 + 623 + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], 624 + 625 + "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], 626 + 627 + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 628 + 629 + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], 630 + 631 + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], 632 + 633 + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], 634 + 635 + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], 636 + 637 + "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], 638 + 639 + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], 640 + 641 + "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], 642 + 643 + "franc-min": ["franc-min@6.2.0", "", { "dependencies": { "trigram-utils": "^2.0.0" } }, "sha512-1uDIEUSlUZgvJa2AKYR/dmJC66v/PvGQ9mWfI9nOr/kPpMFyvswK0gPXOwpYJYiYD008PpHLkGfG58SPjQJFxw=="], 644 + 645 + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], 646 + 647 + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], 648 + 649 + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 650 + 651 + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], 652 + 653 + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], 654 + 655 + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], 656 + 657 + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], 658 + 659 + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], 660 + 661 + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], 662 + 663 + "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], 664 + 665 + "get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="], 666 + 667 + "get-uri": ["get-uri@6.0.5", "", { "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" } }, "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg=="], 668 + 669 + "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], 670 + 671 + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], 672 + 673 + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], 674 + 675 + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], 676 + 677 + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], 678 + 679 + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], 680 + 681 + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 682 + 683 + "headers-polyfill": ["headers-polyfill@3.3.0", "", {}, "sha512-5e57etwBpNcDc0b6KCVWEh/Ro063OxPvzVimUdM0/tsYM/T7Hfy3kknIGj78SFTOhNd8AZY41U8mOHoO4LzmIQ=="], 684 + 685 + "html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], 686 + 687 + "htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="], 688 + 689 + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], 690 + 691 + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], 692 + 693 + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], 694 + 695 + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], 696 + 697 + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], 698 + 699 + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], 700 + 701 + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], 702 + 703 + "inquirer": ["inquirer@13.2.2", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/prompts": "^8.2.0", "@inquirer/type": "^4.0.3", "mute-stream": "^3.0.0", "run-async": "^4.0.6", "rxjs": "^7.8.2" }, "peerDependencies": { "@types/node": ">=18" } }, "sha512-+hlN8I88JE9T3zjWHGnMhryniRDbSgFNJHJTyD2iKO5YNpMRyfghQ6wVoe+gV4ygMM4r4GzlsBxNa1g/UUZixA=="], 704 + 705 + "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], 706 + 707 + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], 708 + 709 + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], 710 + 711 + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], 712 + 713 + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 714 + 715 + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], 716 + 717 + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 718 + 719 + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], 720 + 721 + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], 722 + 723 + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], 724 + 725 + "iso-639-1": ["iso-639-1@3.1.5", "", {}, "sha512-gXkz5+KN7HrG0Q5UGqSMO2qB9AsbEeyLP54kF1YrMsIxmu+g4BdB7rflReZTSTZGpfj8wywu6pfPBCylPIzGQA=="], 726 + 727 + "iso-datestring-validator": ["iso-datestring-validator@2.2.2", "", {}, "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA=="], 728 + 729 + "jiti": ["jiti@1.21.7", "", { "bin": "bin/jiti.js" }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], 730 + 731 + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], 732 + 733 + "jsesc": ["jsesc@3.1.0", "", { "bin": "bin/jsesc" }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], 734 + 735 + "json-stable-stringify": ["json-stable-stringify@1.3.0", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg=="], 736 + 737 + "json5": ["json5@2.2.3", "", { "bin": "lib/cli.js" }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], 738 + 739 + "jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="], 740 + 741 + "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="], 742 + 743 + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], 744 + 745 + "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], 746 + 747 + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], 748 + 749 + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], 750 + 751 + "linkedom": ["linkedom@0.18.12", "", { "dependencies": { "css-select": "^5.1.0", "cssom": "^0.5.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "uhyphen": "^0.2.0" }, "peerDependencies": { "canvas": ">= 2" }, "optionalPeers": ["canvas"] }, "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q=="], 752 + 753 + "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], 754 + 755 + "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], 756 + 757 + "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="], 758 + 759 + "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="], 760 + 761 + "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], 762 + 763 + "lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="], 764 + 765 + "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="], 766 + 767 + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], 768 + 769 + "lucide-react": ["lucide-react@0.553.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-BRgX5zrWmNy/lkVAe0dXBgd7XQdZ3HTf+Hwe3c9WK6dqgnj9h+hxV+MDncM88xDWlCq27+TKvHGE70ViODNILw=="], 770 + 771 + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], 772 + 773 + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], 774 + 775 + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], 776 + 777 + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], 778 + 779 + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], 780 + 781 + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], 782 + 783 + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], 784 + 785 + "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], 786 + 787 + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], 788 + 789 + "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], 790 + 791 + "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], 792 + 793 + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 794 + 795 + "multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 796 + 797 + "mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], 798 + 799 + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], 800 + 801 + "n-gram": ["n-gram@2.0.2", "", {}, "sha512-S24aGsn+HLBxUGVAUFOwGpKs7LBcG4RudKU//eWzt/mQ97/NMKQxDWHyHx63UNWk/OOdihgmzoETn1tf5nQDzQ=="], 802 + 803 + "nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 804 + 805 + "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], 806 + 807 + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], 808 + 809 + "netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="], 810 + 811 + "node-abi": ["node-abi@3.87.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ=="], 812 + 813 + "node-cron": ["node-cron@4.2.1", "", {}, "sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg=="], 814 + 815 + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], 816 + 817 + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], 818 + 819 + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], 820 + 821 + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], 822 + 823 + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], 824 + 825 + "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], 826 + 827 + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], 828 + 829 + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], 830 + 831 + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], 832 + 833 + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], 834 + 835 + "otpauth": ["otpauth@9.5.0", "", { "dependencies": { "@noble/hashes": "2.0.1" } }, "sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA=="], 836 + 837 + "pac-proxy-agent": ["pac-proxy-agent@7.2.0", "", { "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", "socks-proxy-agent": "^8.0.5" } }, "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA=="], 838 + 839 + "pac-resolver": ["pac-resolver@7.0.1", "", { "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" } }, "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg=="], 840 + 841 + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], 842 + 843 + "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="], 844 + 845 + "parse5-parser-stream": ["parse5-parser-stream@7.1.2", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="], 846 + 847 + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], 848 + 849 + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], 850 + 851 + "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], 852 + 853 + "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], 854 + 855 + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 856 + 857 + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 858 + 859 + "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], 860 + 861 + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], 862 + 863 + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], 864 + 865 + "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], 866 + 867 + "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="], 868 + 869 + "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="], 870 + 871 + "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], 872 + 873 + "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], 874 + 875 + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], 876 + 877 + "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": "bin.js" }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], 878 + 879 + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], 880 + 881 + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], 882 + 883 + "proxy-agent": ["proxy-agent@6.5.0", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", "socks-proxy-agent": "^8.0.5" } }, "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A=="], 884 + 885 + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], 886 + 887 + "psl": ["psl@1.15.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w=="], 888 + 889 + "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], 890 + 891 + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], 892 + 893 + "puppeteer-core": ["puppeteer-core@24.37.3", "", { "dependencies": { "@puppeteer/browsers": "2.12.1", "chromium-bidi": "14.0.0", "debug": "^4.4.3", "devtools-protocol": "0.0.1566079", "typed-query-selector": "^2.12.0", "webdriver-bidi-protocol": "0.4.1", "ws": "^8.19.0" } }, "sha512-fokQ8gv+hNgsRWqVuP5rUjGp+wzV5aMTP3fcm8ekNabmLGlJdFHas1OdMscAH9Gzq4Qcf7cfI/Pe6wEcAqQhqg=="], 894 + 895 + "qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="], 896 + 897 + "querystringify": ["querystringify@2.2.0", "", {}, "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="], 898 + 899 + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], 900 + 901 + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], 902 + 903 + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], 904 + 905 + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": "cli.js" }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], 906 + 907 + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], 908 + 909 + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], 910 + 911 + "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], 912 + 913 + "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], 914 + 915 + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], 916 + 917 + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], 918 + 919 + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], 920 + 921 + "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], 922 + 923 + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], 924 + 925 + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], 926 + 927 + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], 928 + 929 + "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], 930 + 931 + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], 932 + 933 + "run-async": ["run-async@4.0.6", "", {}, "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ=="], 934 + 935 + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], 936 + 937 + "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], 938 + 939 + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], 940 + 941 + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], 942 + 943 + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], 944 + 945 + "semver": ["semver@7.7.4", "", { "bin": "bin/semver.js" }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], 946 + 947 + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], 948 + 949 + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], 950 + 951 + "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], 952 + 953 + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], 954 + 955 + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], 956 + 957 + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], 958 + 959 + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], 960 + 961 + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], 962 + 963 + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], 964 + 965 + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], 966 + 967 + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], 968 + 969 + "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], 970 + 971 + "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], 972 + 973 + "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], 974 + 975 + "socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="], 976 + 977 + "socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], 978 + 979 + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], 980 + 981 + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 982 + 983 + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], 984 + 985 + "streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="], 986 + 987 + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], 988 + 989 + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], 990 + 991 + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], 992 + 993 + "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], 994 + 995 + "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], 996 + 997 + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], 998 + 999 + "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], 1000 + 1001 + "tailwindcss": ["tailwindcss@3.4.19", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="], 1002 + 1003 + "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], 1004 + 1005 + "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], 1006 + 1007 + "text-decoder": ["text-decoder@1.2.6", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-27FeW5GQFDfw0FpwMQhMagB7BztOOlmjcSRi97t2oplhKVTZtp0DZbSegSaXS5IIC6mxMvBG4AR1Sgc6BX3CQg=="], 1008 + 1009 + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], 1010 + 1011 + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], 1012 + 1013 + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 1014 + 1015 + "tlds": ["tlds@1.261.0", "", { "bin": "bin.js" }, "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA=="], 1016 + 1017 + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 1018 + 1019 + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], 1020 + 1021 + "tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="], 1022 + 1023 + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], 1024 + 1025 + "trigram-utils": ["trigram-utils@2.0.1", "", { "dependencies": { "collapse-white-space": "^2.0.0", "n-gram": "^2.0.0" } }, "sha512-nfWIXHEaB+HdyslAfMxSqWKDdmqY9I32jS7GnqpdWQnLH89r6A5sdk3fDVYqGAZ0CrT8ovAFSAo6HRiWcWNIGQ=="], 1026 + 1027 + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], 1028 + 1029 + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 1030 + 1031 + "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": "dist/cli.mjs" }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], 1032 + 1033 + "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], 1034 + 1035 + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], 1036 + 1037 + "typed-query-selector": ["typed-query-selector@2.12.0", "", {}, "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg=="], 1038 + 1039 + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 1040 + 1041 + "uhyphen": ["uhyphen@0.2.0", "", {}, "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA=="], 1042 + 1043 + "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="], 1044 + 1045 + "undici": ["undici@7.22.0", "", {}, "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg=="], 1046 + 1047 + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 1048 + 1049 + "unicode-segmenter": ["unicode-segmenter@0.14.5", "", {}, "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="], 1050 + 1051 + "universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="], 1052 + 1053 + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], 1054 + 1055 + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], 1056 + 1057 + "url-parse": ["url-parse@1.5.10", "", { "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="], 1058 + 1059 + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], 1060 + 1061 + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], 1062 + 1063 + "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "yaml"], "bin": "bin/vite.js" }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], 1064 + 1065 + "webdriver-bidi-protocol": ["webdriver-bidi-protocol@0.4.1", "", {}, "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw=="], 1066 + 1067 + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], 1068 + 1069 + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], 1070 + 1071 + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], 1072 + 1073 + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], 1074 + 1075 + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], 1076 + 1077 + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], 1078 + 1079 + "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], 1080 + 1081 + "x-client-transaction-id": ["x-client-transaction-id@0.1.9", "", { "dependencies": { "linkedom": "^0.18.9" } }, "sha512-CES4zgkJ0wbfFWm0qgdKphthyb+L7lVHymgOY15v6ivcWSx5p9lp5kzAed+BuqJSP7bS0GbQyJ16ONkRthgsUw=="], 1082 + 1083 + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], 1084 + 1085 + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], 1086 + 1087 + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], 1088 + 1089 + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], 1090 + 1091 + "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], 1092 + 1093 + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], 1094 + 1095 + "@babel/core/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], 1096 + 1097 + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], 1098 + 1099 + "@inquirer/external-editor/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], 1100 + 1101 + "@puppeteer/browsers/tar-fs": ["tar-fs@3.1.1", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="], 1102 + 1103 + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1104 + 1105 + "body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], 1106 + 1107 + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 1108 + 1109 + "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 1110 + 1111 + "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 1112 + 1113 + "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], 1114 + 1115 + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 1116 + 1117 + "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], 1118 + 1119 + "htmlparser2/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], 1120 + 1121 + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1122 + 1123 + "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], 1124 + 1125 + "proxy-agent/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], 1126 + 1127 + "raw-body/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], 1128 + 1129 + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1130 + 1131 + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], 1132 + 1133 + "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 1134 + 1135 + "@puppeteer/browsers/tar-fs/tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="], 1136 + 1137 + "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 1138 + 1139 + "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 1140 + 1141 + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 1142 + 1143 + "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], 1144 + 1145 + "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 1146 + 1147 + "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 1148 + 1149 + "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 1150 + } 1151 + }
+73 -5
install-server.sh
··· 13 13 TS_HOSTNAME="" 14 14 USE_FUNNEL=0 15 15 INSTALL_ARGS=() 16 + BUN_BIN="" 16 17 17 18 usage() { 18 19 cat <<'USAGE' ··· 51 52 fi 52 53 } 53 54 55 + ensure_bun_runtime() { 56 + install_latest_bun() { 57 + if command -v curl >/dev/null 2>&1; then 58 + curl -fsSL https://bun.sh/install | bash >/dev/null 59 + return 0 60 + fi 61 + if command -v wget >/dev/null 2>&1; then 62 + wget -qO- https://bun.sh/install | bash >/dev/null 63 + return 0 64 + fi 65 + 66 + echo "❌ Bun is required, and curl/wget is unavailable for auto-install." 67 + echo " Install Bun manually: https://bun.com/docs/installation" 68 + exit 1 69 + } 70 + 71 + resolve_bun_bin() { 72 + if command -v bun >/dev/null 2>&1; then 73 + command -v bun 74 + return 0 75 + fi 76 + if [[ -x "${HOME}/.bun/bin/bun" ]]; then 77 + printf '%s\n' "${HOME}/.bun/bin/bun" 78 + return 0 79 + fi 80 + return 1 81 + } 82 + 83 + if ! BUN_BIN="$(resolve_bun_bin)"; then 84 + echo "📦 Bun not found. Installing latest Bun..." 85 + install_latest_bun 86 + BUN_BIN="$(resolve_bun_bin || true)" 87 + fi 88 + 89 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 90 + echo "❌ Bun could not be resolved." 91 + echo " Install Bun manually: https://bun.com/docs/installation" 92 + exit 1 93 + fi 94 + 95 + export PATH="$(dirname "$BUN_BIN"):$PATH" 96 + 97 + if ! "$BUN_BIN" upgrade >/dev/null 2>&1; then 98 + echo "⚠️ Bun auto-upgrade failed. Reinstalling latest Bun..." 99 + install_latest_bun 100 + BUN_BIN="$(resolve_bun_bin || true)" 101 + fi 102 + 103 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 104 + echo "❌ Bun could not be resolved after auto-upgrade." 105 + echo " Install Bun manually: https://bun.com/docs/installation" 106 + exit 1 107 + fi 108 + 109 + export PATH="$(dirname "$BUN_BIN"):$PATH" 110 + 111 + local bun_major 112 + bun_major="$($BUN_BIN --version | awk -F. '{print $1}' 2>/dev/null || echo 0)" 113 + if [[ "$bun_major" -lt 1 ]]; then 114 + echo "❌ Bun 1.x+ is required. Current: $($BUN_BIN --version 2>/dev/null || echo 'unknown')" 115 + exit 1 116 + fi 117 + } 118 + 119 + run_bun() { 120 + "$BUN_BIN" "$@" 121 + } 122 + 54 123 is_valid_port() { 55 124 local candidate="$1" 56 125 [[ "$candidate" =~ ^[0-9]+$ ]] || return 1 ··· 59 128 60 129 is_local_port_free() { 61 130 local port="$1" 62 - node -e ' 131 + run_bun -e ' 63 132 const net = require("node:net"); 64 133 const port = Number(process.argv[1]); 65 134 const server = net.createServer(); ··· 259 328 return 0 260 329 fi 261 330 262 - printf '%s' "$json" | node -e ' 331 + printf '%s' "$json" | run_bun -e ' 263 332 const fs = require("node:fs"); 264 333 try { 265 334 const data = JSON.parse(fs.readFileSync(0, "utf8")); ··· 359 428 return 0 360 429 fi 361 430 362 - printf '%s' "$json" | node -e ' 431 + printf '%s' "$json" | run_bun -e ' 363 432 const fs = require("node:fs"); 364 433 try { 365 434 const data = JSON.parse(fs.readFileSync(0, "utf8")); ··· 481 550 ensure_linux 482 551 ensure_sudo 483 552 require_command bash 484 - require_command node 485 - require_command npm 486 553 require_command git 554 + ensure_bun_runtime 487 555 488 556 ensure_local_only_env 489 557 run_app_install
+89 -31
install.sh
··· 25 25 APP_HOST="" 26 26 ACTIVE_RUNNER="" 27 27 CREATED_JWT_SECRET=0 28 + BUN_BIN="" 28 29 29 30 usage() { 30 31 cat <<'USAGE' ··· 46 47 --nohup Force nohup runner 47 48 --port <number> Set or override PORT in .env 48 49 --host <bind-host> Set or override HOST in .env (for example 127.0.0.1) 49 - --skip-install Skip npm install 50 - --skip-build Skip npm run build 50 + --skip-install Skip bun install 51 + --skip-build Skip bun run build 51 52 --skip-native-rebuild Skip native-module compatibility rebuild checks 52 53 -h, --help Show this help 53 54 USAGE ··· 67 68 (( candidate >= 1 && candidate <= 65535 )) 68 69 } 69 70 70 - check_node_version() { 71 - local node_major 72 - node_major="$(node -p "Number(process.versions.node.split('.')[0])" 2>/dev/null || echo 0)" 73 - if [[ "$node_major" -lt 22 ]]; then 74 - echo "Node.js 22+ is required. Current: $(node -v 2>/dev/null || echo 'unknown')" 71 + ensure_bun_runtime() { 72 + install_latest_bun() { 73 + if command -v curl >/dev/null 2>&1; then 74 + curl -fsSL https://bun.sh/install | bash >/dev/null 75 + return 0 76 + fi 77 + if command -v wget >/dev/null 2>&1; then 78 + wget -qO- https://bun.sh/install | bash >/dev/null 79 + return 0 80 + fi 81 + 82 + echo "❌ Bun is required, and curl/wget is unavailable for auto-install." 83 + echo " Install Bun manually: https://bun.com/docs/installation" 75 84 exit 1 85 + } 86 + 87 + resolve_bun_bin() { 88 + if command -v bun >/dev/null 2>&1; then 89 + command -v bun 90 + return 0 91 + fi 92 + if [[ -x "${HOME}/.bun/bin/bun" ]]; then 93 + printf '%s\n' "${HOME}/.bun/bin/bun" 94 + return 0 95 + fi 96 + return 1 97 + } 98 + 99 + if ! BUN_BIN="$(resolve_bun_bin)"; then 100 + echo "📦 Bun not found. Installing latest Bun..." 101 + install_latest_bun 102 + BUN_BIN="$(resolve_bun_bin || true)" 76 103 fi 104 + 105 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 106 + echo "❌ Bun could not be resolved." 107 + echo " Install Bun manually: https://bun.com/docs/installation" 108 + exit 1 109 + fi 110 + 111 + export PATH="$(dirname "$BUN_BIN"):$PATH" 112 + 113 + if ! "$BUN_BIN" upgrade >/dev/null 2>&1; then 114 + echo "⚠️ Bun auto-upgrade failed. Reinstalling latest Bun..." 115 + install_latest_bun 116 + BUN_BIN="$(resolve_bun_bin || true)" 117 + fi 118 + 119 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 120 + echo "❌ Bun could not be resolved after auto-upgrade." 121 + echo " Install Bun manually: https://bun.com/docs/installation" 122 + exit 1 123 + fi 124 + 125 + export PATH="$(dirname "$BUN_BIN"):$PATH" 126 + 127 + local bun_major 128 + bun_major="$($BUN_BIN --version | awk -F. '{print $1}' 2>/dev/null || echo 0)" 129 + if [[ "$bun_major" -lt 1 ]]; then 130 + echo "❌ Bun 1.x+ is required. Current: $($BUN_BIN --version 2>/dev/null || echo 'unknown')" 131 + exit 1 132 + fi 133 + } 134 + 135 + run_bun() { 136 + "$BUN_BIN" "$@" 77 137 } 78 138 79 139 acquire_lock() { ··· 164 224 existing_secret="$(get_env_value JWT_SECRET)" 165 225 if [[ -z "$existing_secret" ]]; then 166 226 local generated_secret 167 - generated_secret="$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")" 227 + generated_secret="$(run_bun -e "console.log(require('crypto').randomBytes(32).toString('hex'))")" 168 228 upsert_env_value JWT_SECRET "$generated_secret" 169 229 CREATED_JWT_SECRET=1 170 230 fi ··· 172 232 173 233 ensure_node_modules_present() { 174 234 if [[ ! -d "$SCRIPT_DIR/node_modules" ]]; then 175 - echo "node_modules not found. Run ./install.sh (without --start-only) first." 235 + echo "Dependencies not found. Run ./install.sh (without --start-only) first." 176 236 exit 1 177 237 fi 178 238 } 179 239 180 240 native_module_compatible() { 181 - node -e "try{require('better-sqlite3');process.exit(0)}catch(e){console.error(e && e.message ? e.message : e);process.exit(1)}" >/dev/null 2>&1 241 + run_bun -e "try{require('better-sqlite3');process.exit(0)}catch(e){console.error(e && e.message ? e.message : e);process.exit(1)}" >/dev/null 2>&1 182 242 } 183 243 184 244 run_native_rebuild() { 185 - echo "Verifying native modules for Node $(node -v)..." 186 - 187 - if npm run rebuild:native; then 188 - return 0 189 - fi 245 + echo "Verifying native modules for Bun $($BUN_BIN --version)..." 190 246 191 - echo "rebuild:native failed. Falling back to direct better-sqlite3 rebuild..." 192 - if npm rebuild better-sqlite3; then 247 + if run_bun run rebuild:native; then 193 248 return 0 194 249 fi 195 250 196 - npm rebuild better-sqlite3 --build-from-source 251 + echo "rebuild:native failed. Forcing fresh Bun install..." 252 + run_bun install --force 197 253 } 198 254 199 255 ensure_native_compatibility() { ··· 205 261 return 0 206 262 fi 207 263 208 - echo "Detected native module mismatch (likely from Node version change)." 264 + echo "Detected native module mismatch (likely from runtime/dependency change)." 209 265 run_native_rebuild 210 266 211 267 if ! native_module_compatible; then 212 268 echo "Native module validation still failed after rebuild." 213 - echo "Try reinstalling dependencies: rm -rf node_modules package-lock.json && npm install" 269 + echo "Try reinstalling dependencies: rm -rf node_modules bun.lock && bun install" 214 270 exit 1 215 271 fi 216 272 } ··· 218 274 ensure_build_artifacts() { 219 275 if [[ ! -f "$SCRIPT_DIR/dist/index.js" ]]; then 220 276 echo "Build output not found (dist/index.js). Running build now." 221 - npm run build 277 + run_bun run build 222 278 fi 223 279 } 224 280 225 281 install_and_build() { 226 282 if [[ "$DO_INSTALL" -eq 1 ]]; then 227 283 echo "Installing dependencies" 228 - npm install --no-audit --no-fund 284 + run_bun install 229 285 fi 230 286 231 287 ensure_node_modules_present ··· 233 289 234 290 if [[ "$DO_BUILD" -eq 1 ]]; then 235 291 echo "Building server and web app" 236 - npm run build 292 + run_bun run build 237 293 fi 238 294 } 239 295 ··· 241 297 local pid="$1" 242 298 local cmd 243 299 cmd="$(ps -p "$pid" -o command= 2>/dev/null || true)" 244 - [[ "$cmd" == *"dist/index.js"* || "$cmd" == *"npm start"* || "$cmd" == *"$APP_NAME"* ]] 300 + [[ "$cmd" == *"dist/index.js"* || "$cmd" == *"bun run start"* || "$cmd" == *"bun dist/index.js"* || "$cmd" == *"$APP_NAME"* ]] 245 301 } 246 302 247 303 stop_pid_gracefully() { ··· 327 383 stop_nohup_if_running >/dev/null 2>&1 || true 328 384 329 385 echo "Starting with nohup" 330 - nohup npm start >> "$LOG_FILE" 2>&1 & 386 + nohup "$BUN_BIN" run start >> "$LOG_FILE" 2>&1 & 331 387 echo "$!" > "$PID_FILE" 332 388 333 389 local pid ··· 351 407 352 408 if pm2 describe "$APP_NAME" >/dev/null 2>&1; then 353 409 echo "[pm2] Restarting existing process with updated env: $APP_NAME" 354 - pm2 restart "$APP_NAME" --update-env 410 + pm2 restart "$APP_NAME" --update-env --interpreter "$BUN_BIN" || { 411 + echo "[pm2] Restart failed, recreating process with Bun interpreter" 412 + pm2 delete "$APP_NAME" || true 413 + pm2 start dist/index.js --name "$APP_NAME" --cwd "$SCRIPT_DIR" --interpreter "$BUN_BIN" --update-env 414 + } 355 415 else 356 416 echo "[pm2] Starting new process: $APP_NAME (cwd=$SCRIPT_DIR, script=dist/index.js)" 357 - pm2 start dist/index.js --name "$APP_NAME" --cwd "$SCRIPT_DIR" --update-env 417 + pm2 start dist/index.js --name "$APP_NAME" --cwd "$SCRIPT_DIR" --interpreter "$BUN_BIN" --update-env 358 418 fi 359 419 360 420 echo "[pm2] Saving PM2 process list" ··· 398 458 return 0 399 459 fi 400 460 else 401 - if node -e "const http=require('http');const req=http.get('$url',res=>{process.exit(res.statusCode && res.statusCode < 500 ? 0 : 1)});req.setTimeout(1500,()=>{req.destroy();process.exit(1)});req.on('error',()=>process.exit(1));" >/dev/null 2>&1; then 461 + if run_bun -e "const http=require('http');const req=http.get('$url',res=>{process.exit(res.statusCode && res.statusCode < 500 ? 0 : 1)});req.setTimeout(1500,()=>{req.destroy();process.exit(1)});req.on('error',()=>process.exit(1));" >/dev/null 2>&1; then 402 462 return 0 403 463 fi 404 464 fi ··· 578 638 ;; 579 639 esac 580 640 581 - require_command node 582 - require_command npm 583 - check_node_version 641 + ensure_bun_runtime 584 642 585 643 acquire_lock 586 644 trap cleanup EXIT
+10 -9
package.json
··· 2 2 "name": "tweets-2-bsky", 3 3 "version": "2.0.0", 4 4 "description": "A powerful tool to crosspost Tweets to Bluesky, supporting threads, videos, and high-quality images.", 5 + "packageManager": "bun@1.3.9", 5 6 "type": "module", 6 7 "main": "dist/index.js", 7 8 "scripts": { 8 - "setup": "npm install && npm run build", 9 - "build": "npm run build:server && npm run build:web", 9 + "setup": "bun install && bun run build", 10 + "prebuild": "bash scripts/ensure-bun.sh", 11 + "build": "tsc && vite build", 10 12 "build:server": "tsc", 11 13 "build:web": "vite build", 12 14 "rebuild:native": "bash scripts/rebuild-native.sh", 13 - "postinstall": "bash scripts/rebuild-native.sh", 14 - "run:once": "node dist/index.js --run-once --no-web", 15 - "test:twitter-metadata": "tsx scripts/test-twitter-profile-metadata.ts", 16 - "start": "node dist/index.js", 17 - "cli": "tsx src/cli.ts", 18 - "dev": "tsx src/index.ts", 15 + "run:once": "bun dist/index.js --run-once --no-web", 16 + "test:twitter-metadata": "bun scripts/test-twitter-profile-metadata.ts", 17 + "start": "bun dist/index.js", 18 + "cli": "bun src/cli.ts", 19 + "dev": "bun src/index.ts", 19 20 "dev:web": "vite", 20 - "import": "tsx src/index.ts --import-history", 21 + "import": "bun src/index.ts --import-history", 21 22 "lint": "biome check --write .", 22 23 "preview:web": "vite preview", 23 24 "format": "biome format --write .",
+69 -8
repair_pm2.sh
··· 1 - #!/bin/bash 2 - echo "🔧 Repairing PM2 Process Environment..." 1 + #!/usr/bin/env bash 2 + 3 + set -euo pipefail 4 + 5 + echo "🔧 Repairing PM2 process environment..." 6 + 7 + if ! command -v pm2 >/dev/null 2>&1; then 8 + echo "❌ PM2 is not installed or not on PATH." 9 + exit 1 10 + fi 11 + 12 + resolve_bun_bin() { 13 + if command -v bun >/dev/null 2>&1; then 14 + command -v bun 15 + return 0 16 + fi 17 + 18 + if [[ -x "${HOME}/.bun/bin/bun" ]]; then 19 + printf '%s\n' "${HOME}/.bun/bin/bun" 20 + return 0 21 + fi 22 + 23 + return 1 24 + } 25 + 26 + install_latest_bun() { 27 + if command -v curl >/dev/null 2>&1; then 28 + curl -fsSL https://bun.sh/install | bash >/dev/null 29 + return 0 30 + fi 31 + 32 + if command -v wget >/dev/null 2>&1; then 33 + wget -qO- https://bun.sh/install | bash >/dev/null 34 + return 0 35 + fi 36 + 37 + echo "❌ Bun is required, and curl/wget is unavailable for auto-install." 38 + echo " Install Bun manually: https://bun.com/docs/installation" 39 + exit 1 40 + } 41 + 42 + BUN_BIN="$(resolve_bun_bin || true)" 43 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 44 + echo "📦 Bun not found. Installing latest Bun..." 45 + install_latest_bun 46 + BUN_BIN="$(resolve_bun_bin || true)" 47 + fi 48 + 49 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 50 + echo "❌ Bun could not be resolved after install." 51 + exit 1 52 + fi 53 + 54 + if ! "$BUN_BIN" upgrade >/dev/null 2>&1; then 55 + echo "⚠️ Bun auto-upgrade failed. Reinstalling latest Bun..." 56 + install_latest_bun 57 + BUN_BIN="$(resolve_bun_bin || true)" 58 + fi 59 + 60 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 61 + echo "❌ Bun could not be resolved after auto-upgrade." 62 + exit 1 63 + fi 3 64 4 65 PROCESS_NAME="tweets-2-bsky" 5 - if pm2 describe twitter-mirror &> /dev/null; then 6 - PROCESS_NAME="twitter-mirror" 66 + if pm2 describe "twitter-mirror" >/dev/null 2>&1; then 67 + PROCESS_NAME="twitter-mirror" 7 68 fi 8 69 9 70 echo "Found process: $PROCESS_NAME" 10 71 echo "Deleting process..." 11 - pm2 delete $PROCESS_NAME 72 + pm2 delete "$PROCESS_NAME" || true 12 73 13 - echo "Starting process with fresh environment..." 14 - pm2 start dist/index.js --name $PROCESS_NAME 74 + echo "Starting process with fresh environment using Bun interpreter..." 75 + pm2 start dist/index.js --name "$PROCESS_NAME" --interpreter "$BUN_BIN" 15 76 16 77 echo "Saving PM2 list..." 17 78 pm2 save 18 79 19 - echo "✅ Repair complete! The MODULE_NOT_FOUND error should be gone." 80 + echo "✅ Repair complete."
+67
scripts/ensure-bun.sh
··· 1 + #!/usr/bin/env bash 2 + 3 + set -euo pipefail 4 + 5 + resolve_bun_bin() { 6 + if command -v bun >/dev/null 2>&1; then 7 + command -v bun 8 + return 0 9 + fi 10 + 11 + if [[ -x "${HOME}/.bun/bin/bun" ]]; then 12 + printf '%s\n' "${HOME}/.bun/bin/bun" 13 + return 0 14 + fi 15 + 16 + return 1 17 + } 18 + 19 + install_latest_bun() { 20 + if command -v curl >/dev/null 2>&1; then 21 + curl -fsSL https://bun.sh/install | bash >/dev/null 22 + return 0 23 + fi 24 + 25 + if command -v wget >/dev/null 2>&1; then 26 + wget -qO- https://bun.sh/install | bash >/dev/null 27 + return 0 28 + fi 29 + 30 + echo "❌ Bun is required, and curl/wget is unavailable for auto-install." 31 + echo " Install Bun manually: https://bun.com/docs/installation" 32 + exit 1 33 + } 34 + 35 + BUN_BIN="$(resolve_bun_bin || true)" 36 + 37 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 38 + echo "📦 Bun not found. Installing latest Bun..." 39 + install_latest_bun 40 + BUN_BIN="$(resolve_bun_bin || true)" 41 + fi 42 + 43 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 44 + echo "❌ Bun could not be resolved after install." 45 + echo " Install Bun manually: https://bun.com/docs/installation" 46 + exit 1 47 + fi 48 + 49 + if ! "$BUN_BIN" upgrade >/dev/null 2>&1; then 50 + echo "⚠️ Bun auto-upgrade failed. Reinstalling latest Bun..." 51 + install_latest_bun 52 + BUN_BIN="$(resolve_bun_bin || true)" 53 + fi 54 + 55 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 56 + echo "❌ Bun could not be resolved after auto-upgrade." 57 + echo " Install Bun manually: https://bun.com/docs/installation" 58 + exit 1 59 + fi 60 + 61 + bun_major="$($BUN_BIN --version | awk -F. '{print $1}' 2>/dev/null || echo 0)" 62 + if [[ "$bun_major" -lt 1 ]]; then 63 + echo "❌ Bun 1.x+ is required. Current: $($BUN_BIN --version 2>/dev/null || echo 'unknown')" 64 + exit 1 65 + fi 66 + 67 + echo "✅ Bun $($BUN_BIN --version) ready"
+73 -10
scripts/rebuild-native.sh
··· 2 2 3 3 set -euo pipefail 4 4 5 - echo "🔧 Rebuilding native modules for Node $(node -v)..." 5 + resolve_bun_bin() { 6 + if command -v bun >/dev/null 2>&1; then 7 + command -v bun 8 + return 0 9 + fi 10 + 11 + if [[ -x "${HOME}/.bun/bin/bun" ]]; then 12 + printf '%s\n' "${HOME}/.bun/bin/bun" 13 + return 0 14 + fi 15 + 16 + return 1 17 + } 18 + 19 + install_latest_bun() { 20 + if command -v curl >/dev/null 2>&1; then 21 + curl -fsSL https://bun.sh/install | bash >/dev/null 22 + return 0 23 + fi 24 + 25 + if command -v wget >/dev/null 2>&1; then 26 + wget -qO- https://bun.sh/install | bash >/dev/null 27 + return 0 28 + fi 29 + 30 + return 1 31 + } 32 + 33 + declare -a BUN_RUNNER=() 34 + 35 + resolve_bun_runner() { 36 + local bun_bin 37 + bun_bin="$(resolve_bun_bin || true)" 38 + 39 + if [[ -z "$bun_bin" || ! -x "$bun_bin" ]]; then 40 + echo "📦 Bun not found. Installing latest Bun..." 41 + if install_latest_bun; then 42 + bun_bin="$(resolve_bun_bin || true)" 43 + fi 44 + fi 45 + 46 + if [[ -n "$bun_bin" && -x "$bun_bin" ]]; then 47 + if ! "$bun_bin" upgrade >/dev/null 2>&1; then 48 + echo "⚠️ Bun auto-upgrade failed. Reinstalling latest Bun..." 49 + if install_latest_bun; then 50 + bun_bin="$(resolve_bun_bin || true)" 51 + fi 52 + fi 53 + fi 54 + 55 + if [[ -n "$bun_bin" && -x "$bun_bin" ]]; then 56 + BUN_RUNNER=("$bun_bin") 57 + return 0 58 + fi 59 + 60 + if command -v npx >/dev/null 2>&1; then 61 + echo "⚠️ Could not install global Bun automatically. Falling back to npx bun." 62 + BUN_RUNNER=("npx" "bun") 63 + return 0 64 + fi 65 + 66 + echo "❌ Bun is required to rebuild native modules." 67 + echo " Install Bun: https://bun.com/docs/installation" 68 + exit 1 69 + } 6 70 7 - if npm rebuild better-sqlite3 >/dev/null 2>&1; then 8 - echo "✅ better-sqlite3 rebuilt successfully." 9 - exit 0 10 - fi 71 + resolve_bun_runner 11 72 12 - echo "⚠️ Standard rebuild failed. Retrying with --build-from-source..." 13 - if npm rebuild better-sqlite3 --build-from-source; then 14 - echo "✅ better-sqlite3 built from source." 73 + echo "🔧 Rebuilding native modules for Bun..." 74 + echo " Runner: ${BUN_RUNNER[*]}" 75 + 76 + if "${BUN_RUNNER[@]}" install --force; then 77 + echo "✅ Native modules rebuilt via fresh Bun install." 15 78 exit 0 16 79 fi 17 80 18 - echo "❌ Failed to rebuild better-sqlite3 for this Node version." 81 + echo "❌ Failed to rebuild native modules with Bun." 19 82 echo " macOS: run 'xcode-select --install'" 20 83 echo " Debian/Ubuntu: sudo apt-get install -y build-essential python3 make g++" 21 - echo " Then run: npm run rebuild:native" 84 + echo " Then run: bun run rebuild:native" 22 85 exit 1
+94 -14
src/cli.ts
··· 16 16 } from './config-manager.js'; 17 17 import { dbService } from './db.js'; 18 18 import { 19 + applyProfileMirrorSyncState, 19 20 fetchTwitterMirrorProfile, 20 21 syncBlueskyProfileFromTwitter, 21 22 validateBlueskyCredentials, ··· 92 93 return; 93 94 } 94 95 95 - const tsxBin = 96 - process.platform === 'win32' 97 - ? path.join(ROOT_DIR, 'node_modules', '.bin', 'tsx.cmd') 98 - : path.join(ROOT_DIR, 'node_modules', '.bin', 'tsx'); 99 - 100 96 const sourceEntry = path.join(ROOT_DIR, 'src', 'index.ts'); 101 - if (fs.existsSync(tsxBin) && fs.existsSync(sourceEntry)) { 102 - await spawnAndWait(tsxBin, [sourceEntry, ...args], ROOT_DIR); 97 + if (fs.existsSync(sourceEntry)) { 98 + await spawnAndWait(process.execPath, [sourceEntry, ...args], ROOT_DIR); 103 99 return; 104 100 } 105 101 106 - throw new Error('Could not find dist/index.js or tsx runtime. Run npm run build first.'); 102 + throw new Error('Could not find dist/index.js or source runtime entry. Run bun run build first.'); 107 103 }; 108 104 109 105 const ensureMapping = async (mappingRef?: string): Promise<AccountMapping | null> => { ··· 409 405 bskyServiceUrl: bskyAnswers.bskyServiceUrl, 410 406 groupName: metadataAnswers.groupName?.trim() || undefined, 411 407 groupEmoji: metadataAnswers.groupEmoji?.trim() || undefined, 408 + profileSyncSourceUsername: normalizeHandle(metadataAnswers.mirrorSourceUsername || usernames[0] || ''), 412 409 }); 413 410 414 411 const latestConfig = getConfig(); ··· 436 433 bskyIdentifier: createdMapping.bskyIdentifier, 437 434 bskyPassword: createdMapping.bskyPassword, 438 435 bskyServiceUrl: createdMapping.bskyServiceUrl, 436 + previousSync: { 437 + sourceUsername: createdMapping.profileSyncSourceUsername, 438 + mirroredDisplayName: createdMapping.lastMirroredDisplayName, 439 + mirroredDescription: createdMapping.lastMirroredDescription, 440 + avatarUrl: createdMapping.lastMirroredAvatarUrl, 441 + bannerUrl: createdMapping.lastMirroredBannerUrl, 442 + }, 439 443 }); 444 + 445 + const syncedConfig = getConfig(); 446 + const syncedIndex = syncedConfig.mappings.findIndex((entry) => entry.id === createdMapping.id); 447 + if (syncedIndex !== -1) { 448 + const current = syncedConfig.mappings[syncedIndex]; 449 + if (current) { 450 + syncedConfig.mappings[syncedIndex] = applyProfileMirrorSyncState( 451 + current, 452 + metadataAnswers.mirrorSourceUsername, 453 + syncResult, 454 + ); 455 + saveConfig(syncedConfig); 456 + } 457 + } 458 + 440 459 console.log('Mapping added successfully. Bluesky profile mirror sync completed.'); 460 + if (syncResult.skipped) { 461 + console.log('No profile updates were required (Twitter profile is unchanged).'); 462 + } 441 463 if (syncResult.warnings.length > 0) { 442 464 console.log('Profile sync warnings:'); 443 465 for (const warning of syncResult.warnings) { ··· 447 469 } catch (error) { 448 470 console.log('Mapping added successfully, but automatic profile sync failed.'); 449 471 console.log(`Reason: ${error instanceof Error ? error.message : String(error)}`); 450 - console.log('Run `npm run cli -- sync-profile` later to retry.'); 472 + console.log('Run `bun run cli -- sync-profile` later to retry.'); 451 473 } 452 474 }); 453 475 ··· 459 481 const mapping = await ensureMapping(mappingRef); 460 482 if (!mapping) return; 461 483 484 + const availableSources = mapping.twitterUsernames.map(normalizeHandle).filter((username) => username.length > 0); 462 485 const requestedSource = options?.source ? normalizeHandle(options.source) : ''; 463 - if ( 464 - requestedSource && 465 - !mapping.twitterUsernames.some((username) => normalizeHandle(username) === normalizeHandle(requestedSource)) 466 - ) { 486 + if (requestedSource && !availableSources.includes(requestedSource)) { 467 487 console.log(`@${requestedSource} is not part of the selected mapping.`); 468 488 return; 469 489 } 470 490 471 - const sourceTwitterUsername = requestedSource || mapping.twitterUsernames[0]; 491 + const storedSource = normalizeHandle(mapping.profileSyncSourceUsername || ''); 492 + const sourceTwitterUsername = 493 + requestedSource || 494 + (storedSource && availableSources.includes(storedSource) ? storedSource : '') || 495 + (availableSources.length === 1 ? availableSources[0] : ''); 496 + 497 + if (!sourceTwitterUsername && availableSources.length > 1) { 498 + console.log('This mapping has multiple Twitter sources. Set profileSyncSourceUsername first with edit-mapping.'); 499 + return; 500 + } 501 + 472 502 if (!sourceTwitterUsername) { 473 503 console.log('Mapping has no Twitter source usernames.'); 474 504 return; ··· 480 510 bskyIdentifier: mapping.bskyIdentifier, 481 511 bskyPassword: mapping.bskyPassword, 482 512 bskyServiceUrl: mapping.bskyServiceUrl, 513 + previousSync: { 514 + sourceUsername: mapping.profileSyncSourceUsername, 515 + mirroredDisplayName: mapping.lastMirroredDisplayName, 516 + mirroredDescription: mapping.lastMirroredDescription, 517 + avatarUrl: mapping.lastMirroredAvatarUrl, 518 + bannerUrl: mapping.lastMirroredBannerUrl, 519 + }, 483 520 }); 484 521 522 + const config = getConfig(); 523 + const index = config.mappings.findIndex((entry) => entry.id === mapping.id); 524 + if (index !== -1) { 525 + const current = config.mappings[index]; 526 + if (current) { 527 + config.mappings[index] = applyProfileMirrorSyncState(current, sourceTwitterUsername, result); 528 + saveConfig(config); 529 + } 530 + } 531 + 485 532 console.log(`Profile sync completed for ${mapping.bskyIdentifier} from @${result.twitterProfile.username}.`); 533 + if (result.skipped) { 534 + console.log('No profile updates needed (Twitter profile is unchanged).'); 535 + } 486 536 if (result.warnings.length > 0) { 487 537 console.log('Warnings:'); 488 538 for (const warning of result.warnings) { ··· 549 599 const usernames = answers.twitterUsernames 550 600 .split(',') 551 601 .map((username: string) => username.trim()) 602 + .map((username: string) => normalizeHandle(username)) 552 603 .filter((username: string) => username.length > 0); 553 604 605 + if (usernames.length === 0) { 606 + console.log('Please provide at least one Twitter username.'); 607 + return; 608 + } 609 + 610 + let profileSyncSourceUsername = normalizeHandle(mapping.profileSyncSourceUsername || ''); 611 + if (usernames.length === 1) { 612 + profileSyncSourceUsername = usernames[0] || ''; 613 + } else { 614 + const currentSource = usernames.includes(profileSyncSourceUsername) 615 + ? profileSyncSourceUsername 616 + : usernames[0] || ''; 617 + const sourceAnswer = await inquirer.prompt([ 618 + { 619 + type: 'list', 620 + name: 'profileSyncSourceUsername', 621 + message: 'Use which Twitter source for profile syncing?', 622 + choices: usernames.map((username: string) => ({ 623 + name: `@${username}`, 624 + value: username, 625 + })), 626 + default: currentSource, 627 + }, 628 + ]); 629 + profileSyncSourceUsername = normalizeHandle(String(sourceAnswer.profileSyncSourceUsername || '')); 630 + } 631 + 554 632 const index = config.mappings.findIndex((entry) => entry.id === mapping.id); 555 633 if (index === -1) return; 556 634 ··· 565 643 bskyServiceUrl: answers.bskyServiceUrl, 566 644 groupName: answers.groupName?.trim() || undefined, 567 645 groupEmoji: answers.groupEmoji?.trim() || undefined, 646 + profileSyncSourceUsername: profileSyncSourceUsername || undefined, 568 647 }; 569 648 570 649 if (answers.bskyPassword && answers.bskyPassword.trim().length > 0) { ··· 591 670 id: mapping.id, 592 671 owner: mapping.owner || 'System', 593 672 twitter: mapping.twitterUsernames.join(', '), 673 + profileSyncSource: mapping.profileSyncSourceUsername || '--', 594 674 bsky: mapping.bskyIdentifier, 595 675 group: `${mapping.groupEmoji || '📁'} ${mapping.groupName || 'Ungrouped'}`, 596 676 enabled: mapping.enabled,
+33
src/config-manager.ts
··· 55 55 groupName?: string; 56 56 groupEmoji?: string; 57 57 createdByUserId?: string; 58 + profileSyncSourceUsername?: string; 59 + lastProfileSyncAt?: string; 60 + lastMirroredDisplayName?: string; 61 + lastMirroredDescription?: string; 62 + lastMirroredAvatarUrl?: string; 63 + lastMirroredBannerUrl?: string; 58 64 } 59 65 60 66 export interface AccountGroup { ··· 136 142 return value; 137 143 } 138 144 return fallback; 145 + }; 146 + 147 + const normalizeIsoDateString = (value: unknown): string | undefined => { 148 + const normalized = normalizeString(value); 149 + if (!normalized) { 150 + return undefined; 151 + } 152 + 153 + const parsed = Date.parse(normalized); 154 + if (!Number.isFinite(parsed)) { 155 + return undefined; 156 + } 157 + 158 + return new Date(parsed).toISOString(); 139 159 }; 140 160 141 161 const normalizeUserPermissions = (value: unknown, role: UserRole): UserPermissions => { ··· 269 289 270 290 const owner = normalizeString(record.owner); 271 291 const usernames = normalizeTwitterUsernames(record.twitterUsernames, record.twitterUsername); 292 + const profileSyncSourceUsername = normalizeUsername(record.profileSyncSourceUsername); 293 + const resolvedProfileSyncSource = 294 + profileSyncSourceUsername && usernames.includes(profileSyncSourceUsername) 295 + ? profileSyncSourceUsername 296 + : usernames.length === 1 297 + ? usernames[0] 298 + : undefined; 272 299 const explicitCreator = normalizeString(record.createdByUserId) ?? normalizeString(record.ownerUserId); 273 300 const explicitCreatorExists = explicitCreator && users.some((user) => user.id === explicitCreator); 274 301 ··· 282 309 owner, 283 310 groupName: normalizeString(record.groupName), 284 311 groupEmoji: normalizeString(record.groupEmoji), 312 + profileSyncSourceUsername: resolvedProfileSyncSource, 313 + lastProfileSyncAt: normalizeIsoDateString(record.lastProfileSyncAt), 314 + lastMirroredDisplayName: normalizeString(record.lastMirroredDisplayName), 315 + lastMirroredDescription: normalizeString(record.lastMirroredDescription), 316 + lastMirroredAvatarUrl: normalizeString(record.lastMirroredAvatarUrl), 317 + lastMirroredBannerUrl: normalizeString(record.lastMirroredBannerUrl), 285 318 createdByUserId: 286 319 (explicitCreatorExists ? explicitCreator : undefined) ?? matchOwnerToUserId(owner, users) ?? adminUserId, 287 320 };
+44 -15
src/db.ts
··· 1 1 import fs from 'node:fs'; 2 2 import path from 'node:path'; 3 3 import { fileURLToPath } from 'node:url'; 4 - import Database from 'better-sqlite3'; 4 + 5 + interface DbStatement { 6 + get: (...params: any[]) => unknown; 7 + all: (...params: any[]) => unknown[]; 8 + run: (...params: any[]) => unknown; 9 + } 10 + 11 + interface DbLike { 12 + prepare: (sql: string) => DbStatement; 13 + exec: (sql: string) => unknown; 14 + transaction: <T extends (...args: any[]) => any>(fn: T) => T; 15 + pragma?: (sql: string) => unknown; 16 + } 5 17 6 18 const __filename = fileURLToPath(import.meta.url); 7 19 const __dirname = path.dirname(__filename); ··· 11 23 fs.mkdirSync(DB_DIR); 12 24 } 13 25 14 - const db = new Database(path.join(DB_DIR, 'database.sqlite')); 26 + const DB_PATH = path.join(DB_DIR, 'database.sqlite'); 27 + 28 + const db: DbLike = await (async () => { 29 + if (typeof process.versions.bun === 'string') { 30 + const bunSqliteSpecifier = 'bun:sqlite'; 31 + const sqliteModule = (await import(bunSqliteSpecifier)) as { 32 + Database: new (filename: string) => DbLike; 33 + }; 34 + return new sqliteModule.Database(DB_PATH) as unknown as DbLike; 35 + } 36 + 37 + const betterSqliteModule = await import('better-sqlite3'); 38 + return new betterSqliteModule.default(DB_PATH) as unknown as DbLike; 39 + })(); 15 40 16 41 // Enable WAL mode for better concurrency 17 - db.pragma('journal_mode = WAL'); 42 + if (typeof db.pragma === 'function') { 43 + db.pragma('journal_mode = WAL'); 44 + } else { 45 + db.exec('PRAGMA journal_mode = WAL;'); 46 + } 18 47 19 48 // --- Migration Support --- 20 49 const tableInfo = db.prepare('PRAGMA table_info(processed_tweets)').all() as any[]; 21 50 22 51 if (tableInfo.length > 0) { 23 - let schemaChanged = false; 52 + const schemaChanged = false; 24 53 const hasBskyIdentifier = tableInfo.some((col) => col.name === 'bsky_identifier'); 25 54 const hasTweetText = tableInfo.some((col) => col.name === 'tweet_text'); 26 55 const hasTailUri = tableInfo.some((col) => col.name === 'bsky_tail_uri'); 27 56 28 57 if (!hasBskyIdentifier || !hasTweetText || !hasTailUri) { 29 58 console.log('🔄 Upgrading database schema...'); 30 - 59 + 31 60 // SQLite doesn't support easy PK changes, so we recreate the table if identifier is missing 32 61 // Or if we just need to add a column, we can do ALTER TABLE if it's not the PK. 33 62 // However, since we might need to do both or one, let's just do the full migration pattern 34 63 // to be safe and consistent. 35 - 64 + 36 65 db.transaction(() => { 37 66 // 1. Rename existing table 38 67 db.exec(`ALTER TABLE processed_tweets RENAME TO processed_tweets_old;`); ··· 59 88 // 3. Migrate data 60 89 // Handle the case where the old table might not have had bsky_identifier 61 90 const oldColumns = tableInfo.map((c) => c.name); 62 - 91 + 63 92 // Construct the SELECT part based on available old columns 64 93 // If old table didn't have bsky_identifier, we default to 'unknown' 65 94 const identifierSelect = oldColumns.includes('bsky_identifier') ? 'bsky_identifier' : "'unknown'"; 66 - 95 + 67 96 // If old table didn't have tweet_text, we default to NULL 68 - const textSelect = oldColumns.includes('tweet_text') ? 'tweet_text' : "NULL"; 97 + const textSelect = oldColumns.includes('tweet_text') ? 'tweet_text' : 'NULL'; 69 98 70 - const tailUriSelect = oldColumns.includes('bsky_tail_uri') ? 'bsky_tail_uri' : "NULL"; 71 - const tailCidSelect = oldColumns.includes('bsky_tail_cid') ? 'bsky_tail_cid' : "NULL"; 99 + const tailUriSelect = oldColumns.includes('bsky_tail_uri') ? 'bsky_tail_uri' : 'NULL'; 100 + const tailCidSelect = oldColumns.includes('bsky_tail_cid') ? 'bsky_tail_cid' : 'NULL'; 72 101 73 102 db.exec(` 74 103 INSERT INTO processed_tweets ( ··· 288 317 bsky_tail_uri: row.bsky_tail_uri, 289 318 bsky_tail_cid: row.bsky_tail_cid, 290 319 status: row.status, 291 - created_at: row.created_at 320 + created_at: row.created_at, 292 321 }; 293 322 }, 294 323 ··· 322 351 uri: row.bsky_uri, 323 352 cid: row.bsky_cid, 324 353 root: row.bsky_root_uri ? { uri: row.bsky_root_uri, cid: row.bsky_root_cid } : undefined, 325 - tail: (row.bsky_tail_uri && row.bsky_tail_cid) ? { uri: row.bsky_tail_uri, cid: row.bsky_tail_cid } : undefined, 354 + tail: row.bsky_tail_uri && row.bsky_tail_cid ? { uri: row.bsky_tail_uri, cid: row.bsky_tail_cid } : undefined, 326 355 migrated: row.status === 'migrated', 327 356 skipped: row.status === 'skipped', 328 357 }; ··· 339 368 uri: row.bsky_uri, 340 369 cid: row.bsky_cid, 341 370 root: row.bsky_root_uri ? { uri: row.bsky_root_uri, cid: row.bsky_root_cid } : undefined, 342 - tail: (row.bsky_tail_uri && row.bsky_tail_cid) ? { uri: row.bsky_tail_uri, cid: row.bsky_tail_cid } : undefined, 371 + tail: row.bsky_tail_uri && row.bsky_tail_cid ? { uri: row.bsky_tail_uri, cid: row.bsky_tail_cid } : undefined, 343 372 migrated: row.status === 'migrated', 344 - skipped: row.status === 'skipped' 373 + skipped: row.status === 'skipped', 345 374 }; 346 375 } 347 376 return map;
+118 -1
src/index.ts
··· 15 15 import sharp from 'sharp'; 16 16 import { generateAltText } from './ai-manager.js'; 17 17 18 - import { getConfig } from './config-manager.js'; 18 + import { getConfig, saveConfig } from './config-manager.js'; 19 + import { applyProfileMirrorSyncState, syncBlueskyProfileFromTwitter } from './profile-mirror.js'; 19 20 20 21 // ESM __dirname equivalent 21 22 const __filename = fileURLToPath(import.meta.url); ··· 2028 2029 // Task management 2029 2030 const activeTasks = new Map<string, Promise<void>>(); 2030 2031 const DEFAULT_BACKFILL_ACCOUNT_TIMEOUT_MS = 2 * 60 * 1000; 2032 + const PROFILE_SYNC_INTERVAL_MS = 24 * 60 * 60 * 1000; 2033 + let profileSyncStateWriteQueue: Promise<void> = Promise.resolve(); 2031 2034 2032 2035 const describeError = (error: unknown): string => { 2033 2036 if (error instanceof Error) { ··· 2056 2059 } 2057 2060 return DEFAULT_BACKFILL_ACCOUNT_TIMEOUT_MS; 2058 2061 }; 2062 + 2063 + const normalizeMappingHandle = (value: string): string => value.trim().replace(/^@/, '').toLowerCase(); 2064 + 2065 + const parseIsoTimestampMs = (value?: string): number | null => { 2066 + if (!value) { 2067 + return null; 2068 + } 2069 + const parsed = Date.parse(value); 2070 + return Number.isFinite(parsed) ? parsed : null; 2071 + }; 2072 + 2073 + const isProfileSyncDue = (mapping: AccountMapping): boolean => { 2074 + const lastSyncMs = parseIsoTimestampMs(mapping.lastProfileSyncAt); 2075 + if (!lastSyncMs) { 2076 + return true; 2077 + } 2078 + return Date.now() - lastSyncMs >= PROFILE_SYNC_INTERVAL_MS; 2079 + }; 2080 + 2081 + const resolveProfileSyncSourceForMapping = (mapping: AccountMapping): string | null => { 2082 + const candidates = mapping.twitterUsernames.map(normalizeMappingHandle).filter((username) => username.length > 0); 2083 + if (candidates.length === 0) { 2084 + return null; 2085 + } 2086 + if (candidates.length === 1) { 2087 + return candidates[0] || null; 2088 + } 2089 + 2090 + const selected = normalizeMappingHandle(mapping.profileSyncSourceUsername || ''); 2091 + if (selected && candidates.includes(selected)) { 2092 + return selected; 2093 + } 2094 + 2095 + return null; 2096 + }; 2097 + 2098 + const persistProfileSyncResult = ( 2099 + mappingId: string, 2100 + sourceTwitterUsername: string, 2101 + updateResult: Awaited<ReturnType<typeof syncBlueskyProfileFromTwitter>>, 2102 + ) => { 2103 + profileSyncStateWriteQueue = profileSyncStateWriteQueue 2104 + .then(() => { 2105 + const config = getConfig(); 2106 + const index = config.mappings.findIndex((entry) => entry.id === mappingId); 2107 + const mapping = config.mappings[index]; 2108 + if (index === -1 || !mapping) { 2109 + return; 2110 + } 2111 + 2112 + config.mappings[index] = applyProfileMirrorSyncState(mapping, sourceTwitterUsername, updateResult); 2113 + saveConfig(config); 2114 + }) 2115 + .catch((error) => { 2116 + console.error(`[Scheduler] Failed persisting profile sync metadata for mapping ${mappingId}:`, error); 2117 + }); 2118 + 2119 + return profileSyncStateWriteQueue; 2120 + }; 2121 + 2122 + async function maybeSyncMappingProfileInBackground(mapping: AccountMapping, dryRun: boolean, logPrefix: string): Promise<void> { 2123 + if (dryRun) { 2124 + return; 2125 + } 2126 + if (!isProfileSyncDue(mapping)) { 2127 + return; 2128 + } 2129 + 2130 + const sourceTwitterUsername = resolveProfileSyncSourceForMapping(mapping); 2131 + if (!sourceTwitterUsername) { 2132 + if (mapping.twitterUsernames.length > 1) { 2133 + console.warn( 2134 + `${logPrefix} ⚠️ Skipping automatic profile sync: multi-source mapping requires profileSyncSourceUsername selection.`, 2135 + ); 2136 + } 2137 + return; 2138 + } 2139 + 2140 + try { 2141 + console.log(`${logPrefix} 🪞 Running automatic profile sync from @${sourceTwitterUsername}.`); 2142 + const result = await syncBlueskyProfileFromTwitter({ 2143 + twitterUsername: sourceTwitterUsername, 2144 + bskyIdentifier: mapping.bskyIdentifier, 2145 + bskyPassword: mapping.bskyPassword, 2146 + bskyServiceUrl: mapping.bskyServiceUrl, 2147 + previousSync: { 2148 + sourceUsername: mapping.profileSyncSourceUsername, 2149 + mirroredDisplayName: mapping.lastMirroredDisplayName, 2150 + mirroredDescription: mapping.lastMirroredDescription, 2151 + avatarUrl: mapping.lastMirroredAvatarUrl, 2152 + bannerUrl: mapping.lastMirroredBannerUrl, 2153 + }, 2154 + }); 2155 + 2156 + Object.assign(mapping, applyProfileMirrorSyncState(mapping, sourceTwitterUsername, result)); 2157 + await persistProfileSyncResult(mapping.id, sourceTwitterUsername, result); 2158 + 2159 + if (result.skipped) { 2160 + console.log(`${logPrefix} 🪞 Profile sync skipped (no Twitter profile changes).`); 2161 + return; 2162 + } 2163 + 2164 + if (result.warnings.length > 0) { 2165 + console.warn(`${logPrefix} ⚠️ Profile sync completed with ${result.warnings.length} warning(s).`); 2166 + return; 2167 + } 2168 + 2169 + console.log(`${logPrefix} ✅ Profile sync completed.`); 2170 + } catch (error) { 2171 + console.error(`${logPrefix} ❌ Automatic profile sync failed: ${describeError(error)}`); 2172 + } 2173 + } 2059 2174 2060 2175 async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, timeoutMessage: string): Promise<T> { 2061 2176 let timeoutHandle: NodeJS.Timeout | undefined; ··· 2238 2353 console.error(`${logPrefix} ❌ Error checking @${twitterUsername}: ${describeError(err)}`); 2239 2354 } 2240 2355 } 2356 + 2357 + await maybeSyncMappingProfileInBackground(mapping, dryRun, logPrefix); 2241 2358 } 2242 2359 } catch (err) { 2243 2360 sourceErrors += 1;
+261 -12
src/profile-mirror.ts
··· 1 - import { BskyAgent } from '@atproto/api'; 1 + import { BskyAgent, RichText } from '@atproto/api'; 2 2 import type { BlobRef } from '@atproto/api'; 3 3 import { Scraper, type Profile as TwitterProfile } from '@the-convocation/twitter-scraper'; 4 4 import axios from 'axios'; ··· 9 9 const PROFILE_IMAGE_TARGET_BYTES = 950 * 1024; 10 10 const DEFAULT_BSKY_SERVICE_URL = 'https://bsky.social'; 11 11 const BSKY_SETTINGS_URL = 'https://bsky.app/settings/account'; 12 + const BSKY_PUBLIC_APPVIEW_URL = (process.env.BSKY_PUBLIC_APPVIEW_URL || 'https://public.api.bsky.app').replace( 13 + /\/$/, 14 + '', 15 + ); 12 16 const MIRROR_SUFFIX = '{UNOFFICIAL}'; 17 + const FEDIVERSE_BRIDGE_HANDLE = 'ap.brid.gy'; 18 + const MIN_BRIDGE_ACCOUNT_AGE_MS = 7 * 24 * 60 * 60 * 1000; 13 19 14 20 type ProfileImageKind = 'avatar' | 'banner'; 15 21 ··· 49 55 bsky: BlueskyCredentialValidation; 50 56 avatarSynced: boolean; 51 57 bannerSynced: boolean; 58 + skipped: boolean; 59 + changed: { 60 + displayName: boolean; 61 + description: boolean; 62 + avatar: boolean; 63 + banner: boolean; 64 + }; 52 65 warnings: string[]; 53 66 } 54 67 68 + export interface ProfileMirrorSyncState { 69 + sourceUsername?: string; 70 + mirroredDisplayName?: string; 71 + mirroredDescription?: string; 72 + avatarUrl?: string; 73 + bannerUrl?: string; 74 + } 75 + 76 + export interface MappingProfileSyncState { 77 + profileSyncSourceUsername?: string; 78 + lastProfileSyncAt?: string; 79 + lastMirroredDisplayName?: string; 80 + lastMirroredDescription?: string; 81 + lastMirroredAvatarUrl?: string; 82 + lastMirroredBannerUrl?: string; 83 + } 84 + 85 + export interface FediverseBridgeResult { 86 + bsky: BlueskyCredentialValidation; 87 + bridgedAccountHandle: string; 88 + fediverseAddress: string; 89 + accountCreatedAt: string; 90 + ageDays: number; 91 + followedBridgeAccount: boolean; 92 + announcementUri: string; 93 + announcementCid: string; 94 + } 95 + 55 96 const normalizeTwitterUsername = (value: string) => value.trim().replace(/^@/, '').toLowerCase(); 56 97 57 98 const normalizeOptionalString = (value: unknown): string | undefined => { 58 99 if (typeof value !== 'string') return undefined; 59 100 const trimmed = value.trim(); 60 101 return trimmed.length > 0 ? trimmed : undefined; 102 + }; 103 + 104 + const normalizeMirrorStateUrl = (value?: string): string | undefined => normalizeOptionalString(value); 105 + 106 + const toNormalizedMirrorState = (state?: ProfileMirrorSyncState) => ({ 107 + sourceUsername: normalizeTwitterUsername(state?.sourceUsername || ''), 108 + mirroredDisplayName: normalizeOptionalString(state?.mirroredDisplayName), 109 + mirroredDescription: normalizeOptionalString(state?.mirroredDescription), 110 + avatarUrl: normalizeMirrorStateUrl(state?.avatarUrl), 111 + bannerUrl: normalizeMirrorStateUrl(state?.bannerUrl), 112 + }); 113 + 114 + const buildMirrorStateFromTwitterProfile = (twitterProfile: TwitterMirrorProfile): ProfileMirrorSyncState => ({ 115 + sourceUsername: normalizeTwitterUsername(twitterProfile.username), 116 + mirroredDisplayName: twitterProfile.mirroredDisplayName, 117 + mirroredDescription: twitterProfile.mirroredDescription, 118 + avatarUrl: normalizeMirrorStateUrl(twitterProfile.avatarUrl), 119 + bannerUrl: normalizeMirrorStateUrl(twitterProfile.bannerUrl), 120 + }); 121 + 122 + const hasMirrorStateChanges = (previous: ProfileMirrorSyncState | undefined, next: ProfileMirrorSyncState) => { 123 + const normalizedPrevious = toNormalizedMirrorState(previous); 124 + const normalizedNext = toNormalizedMirrorState(next); 125 + 126 + return { 127 + displayName: normalizedPrevious.mirroredDisplayName !== normalizedNext.mirroredDisplayName, 128 + description: normalizedPrevious.mirroredDescription !== normalizedNext.mirroredDescription, 129 + avatar: normalizedPrevious.avatarUrl !== normalizedNext.avatarUrl, 130 + banner: normalizedPrevious.bannerUrl !== normalizedNext.bannerUrl, 131 + }; 61 132 }; 62 133 63 134 const normalizeBskyServiceUrl = (value?: string): string => { ··· 345 416 }; 346 417 }; 347 418 419 + export const applyProfileMirrorSyncState = <T extends MappingProfileSyncState>( 420 + mapping: T, 421 + sourceTwitterUsername: string, 422 + result: MirrorProfileSyncResult, 423 + ): T => { 424 + const normalizedSource = normalizeTwitterUsername(sourceTwitterUsername); 425 + const next: T = { 426 + ...mapping, 427 + profileSyncSourceUsername: normalizedSource || mapping.profileSyncSourceUsername, 428 + lastProfileSyncAt: new Date().toISOString(), 429 + lastMirroredDisplayName: result.twitterProfile.mirroredDisplayName, 430 + lastMirroredDescription: result.twitterProfile.mirroredDescription, 431 + }; 432 + 433 + if (result.changed.avatar && result.avatarSynced) { 434 + next.lastMirroredAvatarUrl = normalizeMirrorStateUrl(result.twitterProfile.avatarUrl); 435 + } 436 + 437 + if (result.changed.banner && result.bannerSynced) { 438 + next.lastMirroredBannerUrl = normalizeMirrorStateUrl(result.twitterProfile.bannerUrl); 439 + } 440 + 441 + return next; 442 + }; 443 + 444 + const fetchPublicProfile = async (actor: string): Promise<{ did: string; handle: string; createdAt?: string }> => { 445 + const normalizedActor = normalizeOptionalString(actor); 446 + if (!normalizedActor) { 447 + throw new Error('Actor is required.'); 448 + } 449 + 450 + const response = await axios.get(`${BSKY_PUBLIC_APPVIEW_URL}/xrpc/app.bsky.actor.getProfile`, { 451 + params: { 452 + actor: normalizedActor, 453 + }, 454 + timeout: 15_000, 455 + }); 456 + 457 + const did = normalizeOptionalString(response.data?.did); 458 + const handle = normalizeOptionalString(response.data?.handle); 459 + if (!did || !handle) { 460 + throw new Error(`Could not resolve Bluesky profile for ${normalizedActor}.`); 461 + } 462 + 463 + return { 464 + did, 465 + handle, 466 + createdAt: normalizeOptionalString(response.data?.createdAt), 467 + }; 468 + }; 469 + 470 + const hasFollowRecordForDid = async (agent: BskyAgent, subjectDid: string): Promise<boolean> => { 471 + const repo = agent.session?.did; 472 + if (!repo) { 473 + throw new Error('Missing Bluesky session DID.'); 474 + } 475 + 476 + let cursor: string | undefined; 477 + let pageCount = 0; 478 + 479 + while (pageCount < 200) { 480 + pageCount += 1; 481 + const response = await agent.com.atproto.repo.listRecords({ 482 + repo, 483 + collection: 'app.bsky.graph.follow', 484 + limit: 100, 485 + cursor, 486 + }); 487 + 488 + const records = Array.isArray(response.data.records) ? response.data.records : []; 489 + for (const record of records) { 490 + const value = record.value as { subject?: string }; 491 + if (typeof value?.subject === 'string' && value.subject === subjectDid) { 492 + return true; 493 + } 494 + } 495 + 496 + cursor = response.data.cursor; 497 + if (!cursor) { 498 + break; 499 + } 500 + } 501 + 502 + return false; 503 + }; 504 + 348 505 const uploadProfileImage = async (agent: BskyAgent, url: string, kind: ProfileImageKind): Promise<BlobRef> => { 349 506 const response = await axios.get<ArrayBuffer>(url, { 350 507 responseType: 'arraybuffer', ··· 366 523 bskyIdentifier: string; 367 524 bskyPassword: string; 368 525 bskyServiceUrl?: string; 526 + previousSync?: ProfileMirrorSyncState; 369 527 }): Promise<MirrorProfileSyncResult> => { 370 528 const twitterProfile = await fetchTwitterMirrorProfile(args.twitterUsername); 529 + const nextMirrorState = buildMirrorStateFromTwitterProfile(twitterProfile); 530 + const changed = hasMirrorStateChanges(args.previousSync, nextMirrorState); 371 531 const bsky = await validateBlueskyCredentials({ 372 532 bskyIdentifier: args.bskyIdentifier, 373 533 bskyPassword: args.bskyPassword, 374 534 bskyServiceUrl: args.bskyServiceUrl, 375 535 }); 376 536 537 + if (!changed.displayName && !changed.description && !changed.avatar && !changed.banner) { 538 + return { 539 + twitterProfile, 540 + bsky, 541 + avatarSynced: false, 542 + bannerSynced: false, 543 + skipped: true, 544 + changed, 545 + warnings: [], 546 + }; 547 + } 548 + 377 549 const agent = new BskyAgent({ service: bsky.serviceUrl }); 378 550 await agent.login({ 379 551 identifier: args.bskyIdentifier, ··· 384 556 let avatarBlob: BlobRef | undefined; 385 557 let bannerBlob: BlobRef | undefined; 386 558 387 - if (twitterProfile.avatarUrl) { 559 + if (changed.avatar && twitterProfile.avatarUrl) { 388 560 try { 389 561 avatarBlob = await uploadProfileImage(agent, twitterProfile.avatarUrl, 'avatar'); 390 562 } catch (error) { 391 563 warnings.push(`Avatar sync failed: ${error instanceof Error ? error.message : String(error)}`); 392 564 } 393 - } else { 565 + } else if (changed.avatar) { 394 566 warnings.push('No Twitter avatar found for this profile.'); 395 567 } 396 568 397 - if (twitterProfile.bannerUrl) { 569 + if (changed.banner && twitterProfile.bannerUrl) { 398 570 try { 399 571 bannerBlob = await uploadProfileImage(agent, twitterProfile.bannerUrl, 'banner'); 400 572 } catch (error) { 401 573 warnings.push(`Banner sync failed: ${error instanceof Error ? error.message : String(error)}`); 402 574 } 403 - } else { 575 + } else if (changed.banner) { 404 576 warnings.push('No Twitter banner found for this profile.'); 405 577 } 406 578 407 - await agent.upsertProfile((existing) => ({ 408 - ...(existing || {}), 409 - displayName: twitterProfile.mirroredDisplayName, 410 - description: twitterProfile.mirroredDescription, 411 - ...(avatarBlob ? { avatar: avatarBlob } : {}), 412 - ...(bannerBlob ? { banner: bannerBlob } : {}), 413 - })); 579 + const shouldUpdateProfile = 580 + changed.displayName || changed.description || Boolean(avatarBlob) || Boolean(bannerBlob); 581 + 582 + if (shouldUpdateProfile) { 583 + await agent.upsertProfile((existing) => ({ 584 + ...(existing || {}), 585 + ...(changed.displayName ? { displayName: twitterProfile.mirroredDisplayName } : {}), 586 + ...(changed.description ? { description: twitterProfile.mirroredDescription } : {}), 587 + ...(avatarBlob ? { avatar: avatarBlob } : {}), 588 + ...(bannerBlob ? { banner: bannerBlob } : {}), 589 + })); 590 + } 414 591 415 592 return { 416 593 twitterProfile, 417 594 bsky, 418 595 avatarSynced: Boolean(avatarBlob), 419 596 bannerSynced: Boolean(bannerBlob), 597 + skipped: false, 598 + changed, 420 599 warnings, 421 600 }; 422 601 }; 602 + 603 + export const bridgeBlueskyAccountToFediverse = async (args: { 604 + bskyIdentifier: string; 605 + bskyPassword: string; 606 + bskyServiceUrl?: string; 607 + }): Promise<FediverseBridgeResult> => { 608 + const bsky = await validateBlueskyCredentials(args); 609 + const accountProfile = await fetchPublicProfile(bsky.did || bsky.handle); 610 + const createdAtRaw = normalizeOptionalString(accountProfile.createdAt); 611 + if (!createdAtRaw) { 612 + throw new Error('Could not determine when this Bluesky account was created.'); 613 + } 614 + 615 + const createdAtMs = Date.parse(createdAtRaw); 616 + if (!Number.isFinite(createdAtMs)) { 617 + throw new Error('Invalid Bluesky account creation timestamp.'); 618 + } 619 + 620 + const ageMs = Date.now() - createdAtMs; 621 + if (ageMs < MIN_BRIDGE_ACCOUNT_AGE_MS) { 622 + const ageDays = Math.floor(ageMs / (24 * 60 * 60 * 1000)); 623 + throw new Error(`Account must be at least 7 days old before bridging (currently ${ageDays} day(s)).`); 624 + } 625 + 626 + const agent = new BskyAgent({ service: bsky.serviceUrl }); 627 + await agent.login({ 628 + identifier: args.bskyIdentifier, 629 + password: args.bskyPassword, 630 + }); 631 + 632 + const bridgeProfile = await fetchPublicProfile(FEDIVERSE_BRIDGE_HANDLE); 633 + const alreadyFollowing = await hasFollowRecordForDid(agent, bridgeProfile.did); 634 + if (!alreadyFollowing) { 635 + const repo = agent.session?.did; 636 + if (!repo) { 637 + throw new Error('Missing Bluesky session DID.'); 638 + } 639 + 640 + await agent.com.atproto.repo.createRecord({ 641 + repo, 642 + collection: 'app.bsky.graph.follow', 643 + record: { 644 + subject: bridgeProfile.did, 645 + createdAt: new Date().toISOString(), 646 + }, 647 + }); 648 + } 649 + 650 + const fediverseAddress = `@${bsky.handle}@bsky.brid.gy`; 651 + const text = `This account can now be found on the fediverse at ${fediverseAddress}`; 652 + const richText = new RichText({ text }); 653 + await richText.detectFacets(agent); 654 + 655 + const post = await agent.post({ 656 + text: richText.text, 657 + facets: richText.facets, 658 + createdAt: new Date().toISOString(), 659 + }); 660 + 661 + return { 662 + bsky, 663 + bridgedAccountHandle: bsky.handle, 664 + fediverseAddress, 665 + accountCreatedAt: new Date(createdAtMs).toISOString(), 666 + ageDays: Math.floor(ageMs / (24 * 60 * 60 * 1000)), 667 + followedBridgeAccount: !alreadyFollowing, 668 + announcementUri: post.uri, 669 + announcementCid: post.cid, 670 + }; 671 + };
+126 -13
src/server.ts
··· 23 23 import { dbService } from './db.js'; 24 24 import type { ProcessedTweet } from './db.js'; 25 25 import { 26 + applyProfileMirrorSyncState, 27 + bridgeBlueskyAccountToFediverse, 26 28 fetchTwitterMirrorProfile, 27 29 syncBlueskyProfileFromTwitter, 28 30 validateBlueskyCredentials, ··· 120 122 handle?: string; 121 123 displayName?: string; 122 124 avatar?: string; 125 + description?: string; 126 + createdAt?: string; 123 127 } 124 128 125 129 interface EnrichedPostMedia { ··· 605 609 handle: typeof profile?.handle === 'string' ? profile.handle : undefined, 606 610 displayName: typeof profile?.displayName === 'string' ? profile.displayName : undefined, 607 611 avatar: typeof profile?.avatar === 'string' ? profile.avatar : undefined, 612 + description: typeof profile?.description === 'string' ? profile.description : undefined, 613 + createdAt: typeof profile?.createdAt === 'string' ? profile.createdAt : undefined, 608 614 }; 609 615 610 616 const keys = [ ··· 1038 1044 return usernames; 1039 1045 }; 1040 1046 1047 + const resolveProfileSyncSourceUsername = (args: { 1048 + twitterUsernames: string[]; 1049 + requestedSource?: unknown; 1050 + fallbackSource?: string; 1051 + }): string | undefined => { 1052 + const twitterUsernames = args.twitterUsernames.map(normalizeActor).filter((username) => username.length > 0); 1053 + if (twitterUsernames.length === 0) { 1054 + return undefined; 1055 + } 1056 + 1057 + const normalizedRequested = 1058 + args.requestedSource !== undefined ? normalizeActor(String(args.requestedSource || '')) : undefined; 1059 + const normalizedFallback = normalizeActor(args.fallbackSource || ''); 1060 + 1061 + let resolved = normalizedRequested; 1062 + if (!resolved && normalizedFallback && twitterUsernames.includes(normalizedFallback)) { 1063 + resolved = normalizedFallback; 1064 + } 1065 + 1066 + if (resolved && twitterUsernames.includes(resolved)) { 1067 + return resolved; 1068 + } 1069 + 1070 + if (twitterUsernames.length === 1) { 1071 + return twitterUsernames[0]; 1072 + } 1073 + 1074 + return undefined; 1075 + }; 1076 + 1077 + const getMappingMirrorSyncState = (mapping: AccountMapping) => ({ 1078 + sourceUsername: mapping.profileSyncSourceUsername, 1079 + mirroredDisplayName: mapping.lastMirroredDisplayName, 1080 + mirroredDescription: mapping.lastMirroredDescription, 1081 + avatarUrl: mapping.lastMirroredAvatarUrl, 1082 + bannerUrl: mapping.lastMirroredBannerUrl, 1083 + }); 1084 + 1041 1085 const getAccessibleGroups = (config: AppConfig, user: AuthenticatedUser) => { 1042 1086 const allGroups = Array.isArray(config.groups) 1043 1087 ? config.groups.filter((group) => getNormalizedGroupKey(group.name) !== RESERVED_UNGROUPED_KEY) ··· 1908 1952 (ownerUser ? getUserPublicLabel(ownerUser) : getActorPublicLabel(req.user)); 1909 1953 const normalizedGroupName = normalizeGroupName(req.body?.groupName); 1910 1954 const normalizedGroupEmoji = normalizeGroupEmoji(req.body?.groupEmoji); 1955 + const profileSyncSourceUsername = resolveProfileSyncSourceUsername({ 1956 + twitterUsernames, 1957 + requestedSource: req.body?.profileSyncSourceUsername, 1958 + }); 1959 + 1960 + if (twitterUsernames.length > 1 && !profileSyncSourceUsername) { 1961 + res.status(400).json({ 1962 + error: 'Select which Twitter source should drive Bluesky profile sync for multi-source mappings.', 1963 + }); 1964 + return; 1965 + } 1911 1966 1912 1967 const newMapping: AccountMapping = { 1913 1968 id: randomUUID(), ··· 1920 1975 groupName: normalizedGroupName || undefined, 1921 1976 groupEmoji: normalizedGroupEmoji || undefined, 1922 1977 createdByUserId, 1978 + profileSyncSourceUsername, 1923 1979 }; 1924 1980 1925 1981 ensureGroupExists(config, normalizedGroupName, normalizedGroupEmoji); ··· 1997 2053 ? normalizeOptionalString(req.body?.owner) || existingMapping.owner 1998 2054 : existingMapping.owner || (ownerUser ? getUserPublicLabel(ownerUser) : undefined); 1999 2055 2056 + const profileSyncSourceUsername = resolveProfileSyncSourceUsername({ 2057 + twitterUsernames, 2058 + requestedSource: req.body?.profileSyncSourceUsername, 2059 + fallbackSource: existingMapping.profileSyncSourceUsername, 2060 + }); 2061 + const sourceWasExplicitlyProvided = req.body?.profileSyncSourceUsername !== undefined; 2062 + const usernamesWereUpdated = req.body?.twitterUsernames !== undefined; 2063 + 2064 + if (twitterUsernames.length > 1 && !profileSyncSourceUsername && (sourceWasExplicitlyProvided || usernamesWereUpdated)) { 2065 + res.status(400).json({ 2066 + error: 'Select which Twitter source should drive Bluesky profile sync for multi-source mappings.', 2067 + }); 2068 + return; 2069 + } 2070 + 2000 2071 const updatedMapping: AccountMapping = { 2001 2072 ...existingMapping, 2002 2073 twitterUsernames, ··· 2007 2078 groupName: nextGroupName, 2008 2079 groupEmoji: nextGroupEmoji, 2009 2080 createdByUserId, 2081 + profileSyncSourceUsername, 2010 2082 }; 2011 2083 2012 2084 ensureGroupExists(config, nextGroupName, nextGroupEmoji); ··· 2018 2090 app.post('/api/mappings/:id/sync-profile-from-twitter', authenticateToken, async (req: any, res) => { 2019 2091 const { id } = req.params; 2020 2092 const config = getConfig(); 2021 - const mapping = config.mappings.find((entry) => entry.id === id); 2093 + const mappingIndex = config.mappings.findIndex((entry) => entry.id === id); 2094 + const mapping = config.mappings[mappingIndex]; 2022 2095 2023 - if (!mapping) { 2096 + if (mappingIndex === -1 || !mapping) { 2024 2097 res.status(404).json({ error: 'Mapping not found' }); 2025 2098 return; 2026 2099 } ··· 2030 2103 return; 2031 2104 } 2032 2105 2033 - const requestedSource = normalizeActor(req.body?.sourceTwitterUsername || ''); 2034 - if ( 2035 - requestedSource && 2036 - !mapping.twitterUsernames.some((username) => normalizeActor(username) === normalizeActor(requestedSource)) 2037 - ) { 2038 - res.status(400).json({ error: 'Selected Twitter source is not part of this mapping.' }); 2039 - return; 2040 - } 2106 + const sourceTwitterUsername = resolveProfileSyncSourceUsername({ 2107 + twitterUsernames: mapping.twitterUsernames, 2108 + requestedSource: req.body?.sourceTwitterUsername, 2109 + fallbackSource: mapping.profileSyncSourceUsername, 2110 + }); 2041 2111 2042 - const sourceTwitterUsername = requestedSource || mapping.twitterUsernames[0]; 2043 2112 if (!sourceTwitterUsername) { 2113 + if (mapping.twitterUsernames.length > 1) { 2114 + res.status(400).json({ 2115 + error: 'Select a profile sync source username before syncing this multi-source mapping.', 2116 + }); 2117 + return; 2118 + } 2119 + 2044 2120 res.status(400).json({ error: 'Mapping has no Twitter source usernames.' }); 2045 2121 return; 2046 2122 } ··· 2051 2127 bskyIdentifier: mapping.bskyIdentifier, 2052 2128 bskyPassword: mapping.bskyPassword, 2053 2129 bskyServiceUrl: mapping.bskyServiceUrl, 2130 + previousSync: getMappingMirrorSyncState(mapping), 2054 2131 }); 2055 2132 2133 + const updatedMapping = applyProfileMirrorSyncState(mapping, sourceTwitterUsername, result); 2134 + config.mappings[mappingIndex] = updatedMapping; 2135 + saveConfig(config); 2136 + 2056 2137 for (const key of [ 2057 - normalizeActor(mapping.bskyIdentifier), 2138 + normalizeActor(updatedMapping.bskyIdentifier), 2058 2139 normalizeActor(result.bsky.handle), 2059 2140 normalizeActor(result.bsky.did), 2060 2141 ]) { ··· 2063 2144 } 2064 2145 } 2065 2146 2066 - res.json({ success: true, ...result }); 2147 + res.json({ 2148 + success: true, 2149 + sourceTwitterUsername, 2150 + mapping: sanitizeMapping(updatedMapping, createUserLookupById(config), req.user), 2151 + ...result, 2152 + }); 2067 2153 } catch (error) { 2068 2154 res.status(400).json({ error: getErrorMessage(error, 'Failed to sync Bluesky profile from Twitter.') }); 2155 + } 2156 + }); 2157 + 2158 + app.post('/api/mappings/:id/bridge-to-fediverse', authenticateToken, async (req: any, res) => { 2159 + const { id } = req.params; 2160 + const config = getConfig(); 2161 + const mapping = config.mappings.find((entry) => entry.id === id); 2162 + 2163 + if (!mapping) { 2164 + res.status(404).json({ error: 'Mapping not found' }); 2165 + return; 2166 + } 2167 + 2168 + if (!canManageMapping(req.user, mapping)) { 2169 + res.status(403).json({ error: 'You do not have permission to bridge this mapping.' }); 2170 + return; 2171 + } 2172 + 2173 + try { 2174 + const result = await bridgeBlueskyAccountToFediverse({ 2175 + bskyIdentifier: mapping.bskyIdentifier, 2176 + bskyPassword: mapping.bskyPassword, 2177 + bskyServiceUrl: mapping.bskyServiceUrl, 2178 + }); 2179 + res.json({ success: true, ...result }); 2180 + } catch (error) { 2181 + res.status(400).json({ error: getErrorMessage(error, 'Failed to bridge account to the fediverse.') }); 2069 2182 } 2070 2183 }); 2071 2184
+81 -27
update.sh
··· 28 28 29 29 BACKUP_SOURCES=() 30 30 BACKUP_PATHS=() 31 + BUN_BIN="" 31 32 32 33 usage() { 33 34 cat <<'USAGE' ··· 43 44 Options: 44 45 --remote <name> Git remote to pull from (default: origin or first remote) 45 46 --branch <name> Git branch to pull (default: current branch or remote HEAD) 46 - --skip-install Skip npm install 47 - --skip-build Skip npm run build 47 + --skip-install Skip bun install 48 + --skip-build Skip bun run build 48 49 --skip-native-rebuild Skip native-module rebuild checks 49 50 --no-restart Do not restart process after update 50 51 -h, --help Show this help ··· 59 60 fi 60 61 } 61 62 62 - check_node_version() { 63 - local node_major 64 - node_major="$(node -p "Number(process.versions.node.split('.')[0])" 2>/dev/null || echo 0)" 65 - if [[ "$node_major" -lt 22 ]]; then 66 - echo "❌ Node.js 22+ is required. Current: $(node -v 2>/dev/null || echo 'unknown')" 63 + ensure_bun_runtime() { 64 + install_latest_bun() { 65 + if command -v curl >/dev/null 2>&1; then 66 + curl -fsSL https://bun.sh/install | bash >/dev/null 67 + return 0 68 + fi 69 + if command -v wget >/dev/null 2>&1; then 70 + wget -qO- https://bun.sh/install | bash >/dev/null 71 + return 0 72 + fi 73 + 74 + echo "❌ Bun is required, and curl/wget is unavailable for auto-install." 75 + echo " Install Bun manually: https://bun.com/docs/installation" 67 76 exit 1 77 + } 78 + 79 + resolve_bun_bin() { 80 + if command -v bun >/dev/null 2>&1; then 81 + command -v bun 82 + return 0 83 + fi 84 + if [[ -x "${HOME}/.bun/bin/bun" ]]; then 85 + printf '%s\n' "${HOME}/.bun/bin/bun" 86 + return 0 87 + fi 88 + return 1 89 + } 90 + 91 + if ! BUN_BIN="$(resolve_bun_bin)"; then 92 + echo "📦 Bun not found. Installing latest Bun..." 93 + install_latest_bun 94 + BUN_BIN="$(resolve_bun_bin || true)" 68 95 fi 96 + 97 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 98 + echo "❌ Bun could not be resolved." 99 + echo " Install Bun manually: https://bun.com/docs/installation" 100 + exit 1 101 + fi 102 + 103 + export PATH="$(dirname "$BUN_BIN"):$PATH" 104 + 105 + if ! "$BUN_BIN" upgrade >/dev/null 2>&1; then 106 + echo "⚠️ Bun auto-upgrade failed. Reinstalling latest Bun..." 107 + install_latest_bun 108 + BUN_BIN="$(resolve_bun_bin || true)" 109 + fi 110 + 111 + if [[ -z "$BUN_BIN" || ! -x "$BUN_BIN" ]]; then 112 + echo "❌ Bun could not be resolved after auto-upgrade." 113 + echo " Install Bun manually: https://bun.com/docs/installation" 114 + exit 1 115 + fi 116 + 117 + export PATH="$(dirname "$BUN_BIN"):$PATH" 118 + 119 + local bun_major 120 + bun_major="$($BUN_BIN --version | awk -F. '{print $1}' 2>/dev/null || echo 0)" 121 + if [[ "$bun_major" -lt 1 ]]; then 122 + echo "❌ Bun 1.x+ is required. Current: $($BUN_BIN --version 2>/dev/null || echo 'unknown')" 123 + exit 1 124 + fi 125 + } 126 + 127 + run_bun() { 128 + "$BUN_BIN" "$@" 69 129 } 70 130 71 131 acquire_lock() { ··· 293 353 } 294 354 295 355 native_module_compatible() { 296 - node -e "try{require('better-sqlite3');process.exit(0)}catch(e){console.error(e && e.message ? e.message : e);process.exit(1)}" >/dev/null 2>&1 356 + run_bun -e "try{require('better-sqlite3');process.exit(0)}catch(e){console.error(e && e.message ? e.message : e);process.exit(1)}" >/dev/null 2>&1 297 357 } 298 358 299 359 rebuild_native_modules() { ··· 306 366 fi 307 367 308 368 echo "🔧 Native module mismatch detected, rebuilding..." 309 - if npm run rebuild:native; then 369 + if run_bun run rebuild:native; then 310 370 return 0 311 371 fi 312 372 313 - echo "⚠️ rebuild:native failed, trying npm rebuild better-sqlite3..." 314 - if npm rebuild better-sqlite3; then 315 - return 0 316 - fi 317 - 318 - npm rebuild better-sqlite3 --build-from-source 373 + echo "⚠️ rebuild:native failed, forcing fresh Bun install..." 374 + run_bun install --force 319 375 } 320 376 321 377 install_dependencies() { ··· 324 380 fi 325 381 326 382 echo "📦 Installing dependencies..." 327 - npm install --no-audit --no-fund 383 + run_bun install 328 384 } 329 385 330 386 build_project() { ··· 333 389 fi 334 390 335 391 echo "🏗️ Building server + web dashboard..." 336 - npm run build 392 + run_bun run build 337 393 } 338 394 339 395 pm2_has_process() { ··· 358 414 359 415 local cmd 360 416 cmd="$(ps -p "$pid" -o command= 2>/dev/null || true)" 361 - [[ "$cmd" == *"dist/index.js"* || "$cmd" == *"npm start"* || "$cmd" == *"$APP_NAME"* ]] 417 + [[ "$cmd" == *"dist/index.js"* || "$cmd" == *"bun run start"* || "$cmd" == *"bun dist/index.js"* || "$cmd" == *"$APP_NAME"* ]] 362 418 } 363 419 364 420 restart_runtime() { ··· 383 439 if [[ "$has_app" -eq 1 && "$has_legacy" -eq 1 ]]; then 384 440 echo "ℹ️ Found both PM2 processes ($APP_NAME and $LEGACY_APP_NAME). Consolidating to $APP_NAME..." 385 441 echo "[pm2] Restarting $APP_NAME with updated environment" 386 - pm2 restart "$APP_NAME" --update-env || { 442 + pm2 restart "$APP_NAME" --update-env --interpreter "$BUN_BIN" || { 387 443 echo "[pm2] Restart failed. Recreating $APP_NAME" 388 444 pm2 delete "$APP_NAME" || true 389 - pm2 start dist/index.js --name "$APP_NAME" --cwd "$SCRIPT_DIR" --update-env 445 + pm2 start dist/index.js --name "$APP_NAME" --cwd "$SCRIPT_DIR" --interpreter "$BUN_BIN" --update-env 390 446 } 391 447 echo "[pm2] Removing duplicate legacy process $LEGACY_APP_NAME" 392 448 pm2 delete "$LEGACY_APP_NAME" || true ··· 398 454 399 455 if [[ "$has_app" -eq 1 ]]; then 400 456 echo "[pm2] Restarting $APP_NAME with updated environment" 401 - pm2 restart "$APP_NAME" --update-env || { 457 + pm2 restart "$APP_NAME" --update-env --interpreter "$BUN_BIN" || { 402 458 echo "⚠️ PM2 restart failed for $APP_NAME. Recreating process..." 403 459 pm2 delete "$APP_NAME" || true 404 - pm2 start dist/index.js --name "$APP_NAME" --cwd "$SCRIPT_DIR" --update-env 460 + pm2 start dist/index.js --name "$APP_NAME" --cwd "$SCRIPT_DIR" --interpreter "$BUN_BIN" --update-env 405 461 } 406 462 echo "[pm2] Saving PM2 process list" 407 463 pm2 save || true ··· 411 467 412 468 if [[ "$has_legacy" -eq 1 ]]; then 413 469 echo "[pm2] Restarting legacy process $LEGACY_APP_NAME with updated environment" 414 - pm2 restart "$LEGACY_APP_NAME" --update-env || { 470 + pm2 restart "$LEGACY_APP_NAME" --update-env --interpreter "$BUN_BIN" || { 415 471 echo "⚠️ PM2 restart failed for $LEGACY_APP_NAME. Recreating it..." 416 472 pm2 delete "$LEGACY_APP_NAME" || true 417 - pm2 start dist/index.js --name "$LEGACY_APP_NAME" --cwd "$SCRIPT_DIR" --update-env 473 + pm2 start dist/index.js --name "$LEGACY_APP_NAME" --cwd "$SCRIPT_DIR" --interpreter "$BUN_BIN" --update-env 418 474 } 419 475 echo "[pm2] Saving PM2 process list" 420 476 pm2 save || true ··· 502 558 echo "=========================" 503 559 504 560 require_command git 505 - require_command node 506 - require_command npm 507 - check_node_version 561 + ensure_bun_runtime 508 562 ensure_git_repo 509 563 510 564 acquire_lock
+427 -42
web/src/App.tsx
··· 58 58 groupEmoji?: string; 59 59 createdByUserId?: string; 60 60 createdByLabel?: string; 61 + profileSyncSourceUsername?: string; 62 + lastProfileSyncAt?: string; 63 + lastMirroredDisplayName?: string; 64 + lastMirroredDescription?: string; 65 + lastMirroredAvatarUrl?: string; 66 + lastMirroredBannerUrl?: string; 61 67 createdByUser?: { 62 68 id: string; 63 69 username?: string; ··· 176 182 handle?: string; 177 183 displayName?: string; 178 184 avatar?: string; 185 + description?: string; 186 + createdAt?: string; 179 187 } 180 188 181 189 interface PendingBackfill { ··· 264 272 bsky: BlueskyCredentialValidation; 265 273 avatarSynced: boolean; 266 274 bannerSynced: boolean; 275 + skipped?: boolean; 276 + changed?: { 277 + displayName: boolean; 278 + description: boolean; 279 + avatar: boolean; 280 + banner: boolean; 281 + }; 267 282 warnings: string[]; 283 + sourceTwitterUsername?: string; 284 + mapping?: AccountMapping; 268 285 } 269 286 270 287 interface BootstrapStatus { ··· 302 319 bskyServiceUrl: string; 303 320 groupName: string; 304 321 groupEmoji: string; 322 + profileSyncSourceUsername: string; 305 323 } 306 324 307 325 interface UserFormState { ··· 331 349 bskyServiceUrl: 'https://bsky.social', 332 350 groupName: '', 333 351 groupEmoji: '📁', 352 + profileSyncSourceUsername: '', 334 353 }); 335 354 336 355 const defaultUserForm = (): UserFormState => ({ ··· 355 374 const ADD_ACCOUNT_STEP_COUNT = ADD_ACCOUNT_STEPS.length; 356 375 const ACCOUNT_SEARCH_MIN_SCORE = 22; 357 376 const DEFAULT_BACKFILL_LIMIT = 15; 377 + const FEDIVERSE_BRIDGE_MIN_AGE_MS = 7 * 24 * 60 * 60 * 1000; 358 378 const DEFAULT_USER_PERMISSIONS: UserPermissions = { 359 379 viewAllMappings: false, 360 380 manageOwnMappings: true, ··· 482 502 return user?.username || user?.email || 'user'; 483 503 } 484 504 505 + function getProfileAgeMs(createdAt?: string): number | null { 506 + if (!createdAt) { 507 + return null; 508 + } 509 + const parsed = Date.parse(createdAt); 510 + if (!Number.isFinite(parsed)) { 511 + return null; 512 + } 513 + return Date.now() - parsed; 514 + } 515 + 516 + function canBridgeToFediverse(createdAt?: string): boolean { 517 + const ageMs = getProfileAgeMs(createdAt); 518 + return ageMs !== null && ageMs >= FEDIVERSE_BRIDGE_MIN_AGE_MS; 519 + } 520 + 485 521 function normalizePermissions(permissions?: Partial<UserPermissions>): UserPermissions { 486 522 return { 487 523 ...DEFAULT_USER_PERMISSIONS, ··· 736 772 const [isMirrorPreviewLoading, setIsMirrorPreviewLoading] = useState(false); 737 773 const [isCredentialValidationBusy, setIsCredentialValidationBusy] = useState(false); 738 774 const [syncingProfileMappingId, setSyncingProfileMappingId] = useState<string | null>(null); 775 + const [isSyncAllProfilesBusy, setIsSyncAllProfilesBusy] = useState(false); 776 + const [bridgingMappingId, setBridgingMappingId] = useState<string | null>(null); 739 777 const [editForm, setEditForm] = useState<MappingFormState>(defaultMappingForm); 740 778 const [editTwitterUsers, setEditTwitterUsers] = useState<string[]>([]); 741 779 const [editTwitterInput, setEditTwitterInput] = useState(''); ··· 875 913 setIsMirrorPreviewLoading(false); 876 914 setIsCredentialValidationBusy(false); 877 915 setSyncingProfileMappingId(null); 916 + setIsSyncAllProfilesBusy(false); 917 + setBridgingMappingId(null); 878 918 setEditTwitterUsers([]); 879 919 setNewGroupName(''); 880 920 setNewGroupEmoji(DEFAULT_GROUP_EMOJI); ··· 1285 1325 }, [newTwitterUsers, selectedMirrorSourceUsername]); 1286 1326 1287 1327 useEffect(() => { 1328 + if (!editingMapping) { 1329 + return; 1330 + } 1331 + 1332 + const candidates = [...new Set(editTwitterUsers.map(normalizeTwitterUsername).filter((username) => username.length > 0))]; 1333 + const selected = normalizeTwitterUsername(editForm.profileSyncSourceUsername || ''); 1334 + if (candidates.length === 0) { 1335 + if (editForm.profileSyncSourceUsername !== '') { 1336 + setEditForm((previous) => ({ ...previous, profileSyncSourceUsername: '' })); 1337 + } 1338 + return; 1339 + } 1340 + 1341 + if (candidates.includes(selected)) { 1342 + return; 1343 + } 1344 + 1345 + const next = candidates.length === 1 ? candidates[0] || '' : ''; 1346 + if (editForm.profileSyncSourceUsername !== next) { 1347 + setEditForm((previous) => ({ ...previous, profileSyncSourceUsername: next })); 1348 + } 1349 + }, [editForm.profileSyncSourceUsername, editTwitterUsers, editingMapping]); 1350 + 1351 + useEffect(() => { 1288 1352 setValidatedBskyCredentials(null); 1289 1353 }, [newMapping.bskyIdentifier, newMapping.bskyPassword, newMapping.bskyServiceUrl]); 1290 1354 ··· 1967 2031 } 1968 2032 }; 1969 2033 1970 - const resolveProfileSyncSource = (mapping: AccountMapping): string | null => { 2034 + const resolveProfileSyncSource = (mapping: AccountMapping, silent = false): string | null => { 1971 2035 const candidates = [ 1972 2036 ...new Set(mapping.twitterUsernames.map(normalizeTwitterUsername).filter((value) => value.length > 0)), 1973 2037 ]; 1974 2038 if (candidates.length === 0) { 1975 - showNotice('error', 'Mapping has no Twitter source usernames.'); 2039 + if (!silent) { 2040 + showNotice('error', 'Mapping has no Twitter source usernames.'); 2041 + } 1976 2042 return null; 1977 2043 } 1978 2044 ··· 1980 2046 return candidates[0] || null; 1981 2047 } 1982 2048 1983 - const typed = window.prompt( 1984 - `This mapping has multiple Twitter sources. Enter one to mirror from:\n${candidates 1985 - .map((username) => `@${username}`) 1986 - .join(', ')}`, 1987 - candidates[0], 1988 - ); 1989 - 1990 - if (typed === null) { 2049 + const selected = normalizeTwitterUsername(mapping.profileSyncSourceUsername || ''); 2050 + if (!selected || !candidates.includes(selected)) { 2051 + if (!silent) { 2052 + showNotice('error', 'Select a profile sync source for this multi-source mapping first.'); 2053 + } 1991 2054 return null; 1992 2055 } 1993 2056 1994 - const selected = normalizeTwitterUsername(typed); 1995 - if (!selected || !candidates.includes(selected)) { 1996 - showNotice('error', 'Please enter one of the mapped Twitter usernames.'); 1997 - return null; 2057 + return selected; 2058 + }; 2059 + 2060 + const syncProfileFromTwitterForMapping = async ( 2061 + mapping: AccountMapping, 2062 + options?: { 2063 + confirm?: boolean; 2064 + showNoticeOnResult?: boolean; 2065 + refreshAfter?: boolean; 2066 + }, 2067 + ): Promise<{ ok: boolean; skipped: boolean }> => { 2068 + if (!authHeaders) { 2069 + return { ok: false, skipped: false }; 2070 + } 2071 + if (!canManageMapping(mapping)) { 2072 + if (options?.showNoticeOnResult !== false) { 2073 + showNotice('error', 'You do not have permission to update this mapping.'); 2074 + } 2075 + return { ok: false, skipped: false }; 2076 + } 2077 + 2078 + const sourceTwitterUsername = resolveProfileSyncSource(mapping, options?.showNoticeOnResult === false); 2079 + if (!sourceTwitterUsername) { 2080 + return { ok: false, skipped: false }; 2081 + } 2082 + 2083 + if (options?.confirm !== false) { 2084 + const confirmed = window.confirm( 2085 + `Sync profile from @${sourceTwitterUsername}? This updates display name, bio, avatar, and banner only when the Twitter profile changed.`, 2086 + ); 2087 + if (!confirmed) { 2088 + return { ok: false, skipped: false }; 2089 + } 1998 2090 } 1999 2091 2000 - return selected; 2092 + setSyncingProfileMappingId(mapping.id); 2093 + try { 2094 + const response = await axios.post<MirrorProfileSyncResult>( 2095 + `/api/mappings/${mapping.id}/sync-profile-from-twitter`, 2096 + { sourceTwitterUsername }, 2097 + { headers: authHeaders }, 2098 + ); 2099 + 2100 + const result = response.data; 2101 + const warnings = result?.warnings || []; 2102 + const skipped = Boolean(result?.skipped); 2103 + 2104 + if (result?.mapping) { 2105 + setMappings((previous) => previous.map((entry) => (entry.id === mapping.id ? result.mapping || entry : entry))); 2106 + } 2107 + 2108 + if (options?.showNoticeOnResult !== false) { 2109 + if (skipped) { 2110 + showNotice('info', `No profile changes detected for @${sourceTwitterUsername}.`); 2111 + } else if (warnings.length > 0) { 2112 + showNotice( 2113 + 'info', 2114 + `Profile synced from @${sourceTwitterUsername} with ${warnings.length} warning(s). First warning: ${warnings[0]}`, 2115 + ); 2116 + } else { 2117 + showNotice('success', `Profile synced from @${sourceTwitterUsername}.`); 2118 + } 2119 + } 2120 + 2121 + if (options?.refreshAfter !== false) { 2122 + await fetchData(); 2123 + } 2124 + 2125 + return { ok: true, skipped }; 2126 + } catch (error) { 2127 + if (options?.showNoticeOnResult !== false) { 2128 + handleAuthFailure(error, 'Failed to sync profile from Twitter.'); 2129 + } 2130 + return { ok: false, skipped: false }; 2131 + } finally { 2132 + setSyncingProfileMappingId((previous) => (previous === mapping.id ? null : previous)); 2133 + } 2001 2134 }; 2002 2135 2003 2136 const handleSyncProfileFromTwitter = async (mapping: AccountMapping) => { 2137 + await syncProfileFromTwitterForMapping(mapping, { 2138 + confirm: true, 2139 + showNoticeOnResult: true, 2140 + refreshAfter: true, 2141 + }); 2142 + }; 2143 + 2144 + const handleSyncAllProfilesFromTwitter = async () => { 2145 + if (!authHeaders) { 2146 + return; 2147 + } 2148 + 2149 + const candidates = accountMappingsForView.filter((mapping) => canManageMapping(mapping)); 2150 + if (candidates.length === 0) { 2151 + showNotice('info', 'No accounts available for profile sync.'); 2152 + return; 2153 + } 2154 + 2155 + const missingSource = candidates.filter( 2156 + (mapping) => mapping.twitterUsernames.length > 1 && !resolveProfileSyncSource(mapping, true), 2157 + ); 2158 + if (missingSource.length > 0) { 2159 + const labels = missingSource 2160 + .slice(0, 3) 2161 + .map((mapping) => mapping.bskyIdentifier) 2162 + .join(', '); 2163 + const suffix = missingSource.length > 3 ? ` and ${missingSource.length - 3} more` : ''; 2164 + showNotice( 2165 + 'error', 2166 + `Sync all failed: choose a profile sync source for ${labels}${suffix} before running bulk sync.`, 2167 + ); 2168 + return; 2169 + } 2170 + 2171 + const confirmed = window.confirm(`Sync Twitter profile data for ${candidates.length} account(s), one at a time?`); 2172 + if (!confirmed) { 2173 + return; 2174 + } 2175 + 2176 + setIsSyncAllProfilesBusy(true); 2177 + let syncedCount = 0; 2178 + let skippedCount = 0; 2179 + let failedCount = 0; 2180 + 2181 + try { 2182 + for (const mapping of candidates) { 2183 + const outcome = await syncProfileFromTwitterForMapping(mapping, { 2184 + confirm: false, 2185 + showNoticeOnResult: false, 2186 + refreshAfter: false, 2187 + }); 2188 + 2189 + if (!outcome.ok) { 2190 + failedCount += 1; 2191 + continue; 2192 + } 2193 + if (outcome.skipped) { 2194 + skippedCount += 1; 2195 + } else { 2196 + syncedCount += 1; 2197 + } 2198 + } 2199 + 2200 + await fetchData(); 2201 + showNotice( 2202 + failedCount > 0 ? 'info' : 'success', 2203 + `Sync all complete: ${syncedCount} updated, ${skippedCount} unchanged, ${failedCount} failed.`, 2204 + ); 2205 + } finally { 2206 + setIsSyncAllProfilesBusy(false); 2207 + } 2208 + }; 2209 + 2210 + const handleUpdateProfileSyncSource = async (mapping: AccountMapping, selectedSource: string) => { 2004 2211 if (!authHeaders) { 2005 2212 return; 2006 2213 } ··· 2009 2216 return; 2010 2217 } 2011 2218 2012 - const sourceTwitterUsername = resolveProfileSyncSource(mapping); 2013 - if (!sourceTwitterUsername) { 2219 + const normalizedSource = normalizeTwitterUsername(selectedSource || ''); 2220 + const sourceExists = mapping.twitterUsernames.some( 2221 + (username) => normalizeTwitterUsername(username) === normalizedSource, 2222 + ); 2223 + const nextSource = sourceExists 2224 + ? normalizedSource 2225 + : mapping.twitterUsernames.length === 1 2226 + ? normalizeTwitterUsername(mapping.twitterUsernames[0] || '') 2227 + : ''; 2228 + 2229 + if (mapping.twitterUsernames.length > 1 && !nextSource) { 2230 + showNotice('error', 'Select one of the mapped Twitter usernames as the profile sync source.'); 2231 + return; 2232 + } 2233 + 2234 + try { 2235 + await axios.put( 2236 + `/api/mappings/${mapping.id}`, 2237 + { 2238 + profileSyncSourceUsername: nextSource, 2239 + }, 2240 + { headers: authHeaders }, 2241 + ); 2242 + 2243 + setMappings((previous) => 2244 + previous.map((entry) => 2245 + entry.id === mapping.id 2246 + ? { 2247 + ...entry, 2248 + profileSyncSourceUsername: nextSource || undefined, 2249 + } 2250 + : entry, 2251 + ), 2252 + ); 2253 + 2254 + showNotice('success', `Profile sync source set to @${nextSource}.`); 2255 + } catch (error) { 2256 + handleAuthFailure(error, 'Failed to update profile sync source.'); 2257 + } 2258 + }; 2259 + 2260 + const handleBridgeToFediverse = async (mapping: AccountMapping) => { 2261 + if (!authHeaders) { 2262 + return; 2263 + } 2264 + if (!canManageMapping(mapping)) { 2265 + showNotice('error', 'You do not have permission to bridge this mapping.'); 2266 + return; 2267 + } 2268 + 2269 + const profile = getProfileForActor(mapping.bskyIdentifier); 2270 + if (!canBridgeToFediverse(profile?.createdAt)) { 2271 + showNotice('error', 'This Bluesky account must be at least 7 days old before bridging.'); 2014 2272 return; 2015 2273 } 2016 2274 2017 2275 const confirmed = window.confirm( 2018 - `Sync profile from @${sourceTwitterUsername}? This replaces display name, bio, avatar, and banner and keeps the {UNOFFICIAL} suffix.`, 2276 + `Bridge ${mapping.bskyIdentifier} to the fediverse now? This follows @ap.brid.gy and posts the bridge announcement.`, 2019 2277 ); 2020 2278 if (!confirmed) { 2021 2279 return; 2022 2280 } 2023 2281 2024 - setSyncingProfileMappingId(mapping.id); 2282 + setBridgingMappingId(mapping.id); 2025 2283 try { 2026 - const response = await axios.post<MirrorProfileSyncResult>( 2027 - `/api/mappings/${mapping.id}/sync-profile-from-twitter`, 2028 - { sourceTwitterUsername }, 2284 + const response = await axios.post<{ fediverseAddress?: string }>( 2285 + `/api/mappings/${mapping.id}/bridge-to-fediverse`, 2286 + {}, 2029 2287 { headers: authHeaders }, 2030 2288 ); 2031 - 2032 - const warnings = response.data?.warnings || []; 2033 - if (warnings.length > 0) { 2034 - showNotice( 2035 - 'info', 2036 - `Profile synced from @${sourceTwitterUsername} with ${warnings.length} warning(s). First warning: ${warnings[0]}`, 2037 - ); 2038 - } else { 2039 - showNotice('success', `Profile synced from @${sourceTwitterUsername}.`); 2040 - } 2041 - 2042 - await fetchData(); 2289 + showNotice( 2290 + 'success', 2291 + `Fediverse bridge enabled: ${response.data?.fediverseAddress || `@${mapping.bskyIdentifier}@bsky.brid.gy`}`, 2292 + ); 2293 + await fetchRecentActivity(); 2043 2294 } catch (error) { 2044 - handleAuthFailure(error, 'Failed to sync profile from Twitter.'); 2295 + handleAuthFailure(error, 'Failed to bridge this account to the fediverse.'); 2045 2296 } finally { 2046 - setSyncingProfileMappingId((previous) => (previous === mapping.id ? null : previous)); 2297 + setBridgingMappingId((previous) => (previous === mapping.id ? null : previous)); 2047 2298 } 2048 2299 }; 2049 2300 ··· 2427 2678 bskyServiceUrl: newMapping.bskyServiceUrl.trim(), 2428 2679 groupName: newMapping.groupName.trim(), 2429 2680 groupEmoji: newMapping.groupEmoji.trim(), 2681 + profileSyncSourceUsername: sourceTwitterUsername, 2430 2682 }, 2431 2683 { headers: authHeaders }, 2432 2684 ); ··· 2531 2783 bskyServiceUrl: mapping.bskyServiceUrl || 'https://bsky.social', 2532 2784 groupName: mapping.groupName || '', 2533 2785 groupEmoji: mapping.groupEmoji || '📁', 2786 + profileSyncSourceUsername: 2787 + mapping.profileSyncSourceUsername || (mapping.twitterUsernames.length === 1 ? mapping.twitterUsernames[0] || '' : ''), 2534 2788 }); 2535 2789 setEditTwitterUsers(mapping.twitterUsernames); 2536 2790 setEditTwitterInput(''); ··· 2548 2802 2549 2803 if (editTwitterUsers.length === 0) { 2550 2804 showNotice('error', 'At least one Twitter username is required.'); 2805 + return; 2806 + } 2807 + 2808 + const normalizedProfileSyncSource = normalizeTwitterUsername(editForm.profileSyncSourceUsername || ''); 2809 + const hasSourceInMapping = editTwitterUsers.some( 2810 + (username) => normalizeTwitterUsername(username) === normalizedProfileSyncSource, 2811 + ); 2812 + const profileSyncSourceUsername = hasSourceInMapping 2813 + ? normalizedProfileSyncSource 2814 + : editTwitterUsers.length === 1 2815 + ? normalizeTwitterUsername(editTwitterUsers[0] || '') 2816 + : ''; 2817 + 2818 + if (editTwitterUsers.length > 1 && !profileSyncSourceUsername) { 2819 + showNotice('error', 'Select which Twitter source should sync this Bluesky profile.'); 2551 2820 return; 2552 2821 } 2553 2822 ··· 2564 2833 bskyServiceUrl: editForm.bskyServiceUrl.trim(), 2565 2834 groupName: editForm.groupName.trim(), 2566 2835 groupEmoji: editForm.groupEmoji.trim(), 2836 + profileSyncSourceUsername, 2567 2837 }, 2568 2838 { headers: authHeaders }, 2569 2839 ); ··· 3254 3524 Add account 3255 3525 </Button> 3256 3526 ) : null} 3527 + <Button 3528 + size="sm" 3529 + variant="outline" 3530 + disabled={isSyncAllProfilesBusy || Boolean(syncingProfileMappingId)} 3531 + onClick={() => { 3532 + void handleSyncAllProfilesFromTwitter(); 3533 + }} 3534 + > 3535 + {isSyncAllProfilesBusy ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <RefreshCw className="mr-2 h-4 w-4" />} 3536 + Sync all 3537 + </Button> 3257 3538 <Badge variant="outline">{accountMappingsForView.length} configured</Badge> 3258 3539 </div> 3259 3540 </div> ··· 3435 3716 const profile = getProfileForActor(mapping.bskyIdentifier); 3436 3717 const profileHandle = profile?.handle || mapping.bskyIdentifier; 3437 3718 const profileName = profile?.displayName || profileHandle; 3719 + const profileBio = profile?.description?.trim() || ''; 3720 + const profileUrl = `https://bsky.app/profile/${profileHandle}`; 3721 + const canManageThisMapping = canManageMapping(mapping); 3722 + const canUseFediverseBridge = canBridgeToFediverse(profile?.createdAt); 3723 + const bridging = bridgingMappingId === mapping.id; 3438 3724 const mappingGroup = getMappingGroupMeta(mapping); 3439 3725 const syncingProfile = syncingProfileMappingId === mapping.id; 3440 3726 ··· 3482 3768 )} 3483 3769 <div className="min-w-0"> 3484 3770 <p className="truncate text-sm font-medium">{profileName}</p> 3485 - <p className="truncate font-mono text-xs text-muted-foreground"> 3771 + <a 3772 + className="inline-flex max-w-full items-center truncate font-mono text-xs text-muted-foreground underline-offset-2 hover:text-foreground hover:underline" 3773 + href={profileUrl} 3774 + target="_blank" 3775 + rel="noreferrer" 3776 + title={`Open @${profileHandle} on Bluesky`} 3777 + > 3486 3778 {profileHandle} 3487 - </p> 3779 + <ArrowUpRight className="ml-1 h-3 w-3 shrink-0" /> 3780 + </a> 3781 + {profileBio ? ( 3782 + <p 3783 + className="mt-1 overflow-hidden text-xs text-muted-foreground" 3784 + style={{ 3785 + display: '-webkit-box', 3786 + WebkitLineClamp: 2, 3787 + WebkitBoxOrient: 'vertical', 3788 + }} 3789 + title={profileBio} 3790 + > 3791 + {profileBio} 3792 + </p> 3793 + ) : null} 3488 3794 </div> 3489 3795 </div> 3490 3796 </td> ··· 3504 3810 <select 3505 3811 className={cn(selectClassName, 'h-9 w-44 px-2 py-1 text-xs')} 3506 3812 value={mappingGroup.key} 3507 - disabled={!canManageMapping(mapping) || !canManageGroupsPermission} 3813 + disabled={ 3814 + !canManageThisMapping || 3815 + !canManageGroupsPermission || 3816 + isSyncAllProfilesBusy || 3817 + Boolean(syncingProfileMappingId) 3818 + } 3508 3819 onChange={(event) => { 3509 3820 void handleAssignMappingGroup(mapping, event.target.value); 3510 3821 }} ··· 3523 3834 </option> 3524 3835 ))} 3525 3836 </select> 3526 - {canManageMapping(mapping) ? ( 3837 + {canManageThisMapping ? ( 3527 3838 <> 3839 + {mapping.twitterUsernames.length > 1 ? ( 3840 + <select 3841 + className={cn(selectClassName, 'h-9 w-44 px-2 py-1 text-xs')} 3842 + value={mapping.profileSyncSourceUsername || ''} 3843 + disabled={ 3844 + isSyncAllProfilesBusy || 3845 + Boolean(syncingProfileMappingId) || 3846 + Boolean(bridgingMappingId) 3847 + } 3848 + onChange={(event) => { 3849 + void handleUpdateProfileSyncSource(mapping, event.target.value); 3850 + }} 3851 + > 3852 + <option value="">Select sync source</option> 3853 + {mapping.twitterUsernames.map((username) => ( 3854 + <option key={`sync-source-${mapping.id}-${username}`} value={username}> 3855 + @{username} 3856 + </option> 3857 + ))} 3858 + </select> 3859 + ) : null} 3528 3860 <Button 3529 3861 variant="outline" 3530 3862 size="sm" ··· 3535 3867 <Button 3536 3868 variant="outline" 3537 3869 size="sm" 3538 - disabled={Boolean(syncingProfileMappingId)} 3870 + disabled={ 3871 + Boolean(syncingProfileMappingId) || 3872 + isSyncAllProfilesBusy || 3873 + Boolean(bridgingMappingId) 3874 + } 3539 3875 onClick={() => { 3540 3876 void handleSyncProfileFromTwitter(mapping); 3541 3877 }} ··· 3547 3883 )} 3548 3884 Sync Profile 3549 3885 </Button> 3886 + {canUseFediverseBridge ? ( 3887 + <Button 3888 + variant="outline" 3889 + size="sm" 3890 + disabled={ 3891 + Boolean(bridgingMappingId) || 3892 + Boolean(syncingProfileMappingId) || 3893 + isSyncAllProfilesBusy 3894 + } 3895 + onClick={() => { 3896 + void handleBridgeToFediverse(mapping); 3897 + }} 3898 + > 3899 + {bridging ? ( 3900 + <Loader2 className="mr-1 h-4 w-4 animate-spin" /> 3901 + ) : ( 3902 + <Repeat2 className="mr-1 h-4 w-4" /> 3903 + )} 3904 + Bridge to Fediverse 3905 + </Button> 3906 + ) : null} 3550 3907 {canQueueBackfillsPermission ? ( 3551 3908 <> 3552 3909 <Button 3553 3910 variant="outline" 3554 3911 size="sm" 3912 + disabled={Boolean(syncingProfileMappingId) || isSyncAllProfilesBusy} 3555 3913 onClick={() => { 3556 3914 void requestBackfill(mapping.id, 'normal'); 3557 3915 }} ··· 3562 3920 <Button 3563 3921 variant="ghost" 3564 3922 size="sm" 3923 + disabled={Boolean(syncingProfileMappingId) || isSyncAllProfilesBusy} 3565 3924 onClick={() => { 3566 3925 void cancelQueuedBackfill(mapping.id); 3567 3926 }} ··· 3573 3932 <Button 3574 3933 variant="subtle" 3575 3934 size="sm" 3935 + disabled={Boolean(syncingProfileMappingId) || isSyncAllProfilesBusy} 3576 3936 onClick={() => { 3577 3937 void requestBackfill(mapping.id, 'reset'); 3578 3938 }} ··· 3586 3946 <Button 3587 3947 variant="destructive" 3588 3948 size="sm" 3949 + disabled={Boolean(syncingProfileMappingId) || isSyncAllProfilesBusy} 3589 3950 onClick={() => { 3590 3951 void handleDeleteAllPosts(mapping.id); 3591 3952 }} ··· 3595 3956 ) : null} 3596 3957 </> 3597 3958 ) : null} 3598 - {canManageMapping(mapping) ? ( 3959 + {canManageThisMapping ? ( 3599 3960 <Button 3600 3961 variant="ghost" 3601 3962 size="sm" 3963 + disabled={Boolean(syncingProfileMappingId) || isSyncAllProfilesBusy} 3602 3964 onClick={() => { 3603 3965 void handleDeleteMapping(mapping.id); 3604 3966 }} ··· 5439 5801 }} 5440 5802 required 5441 5803 /> 5804 + </div> 5805 + <div className="space-y-2"> 5806 + <Label htmlFor="edit-profile-sync-source">Profile Sync Source</Label> 5807 + <select 5808 + id="edit-profile-sync-source" 5809 + className={selectClassName} 5810 + value={editForm.profileSyncSourceUsername} 5811 + onChange={(event) => { 5812 + setEditForm((previous) => ({ ...previous, profileSyncSourceUsername: event.target.value })); 5813 + }} 5814 + > 5815 + {editTwitterUsers.length > 1 ? <option value="">Select source username</option> : null} 5816 + {editTwitterUsers.map((username) => ( 5817 + <option key={`edit-sync-source-${username}`} value={username}> 5818 + @{username} 5819 + </option> 5820 + ))} 5821 + </select> 5822 + {editTwitterUsers.length > 1 ? ( 5823 + <p className="text-xs text-muted-foreground"> 5824 + Required when multiple Twitter usernames map to one Bluesky account. 5825 + </p> 5826 + ) : null} 5442 5827 </div> 5443 5828 <div className="space-y-2"> 5444 5829 <Label htmlFor="edit-bskyPassword">New App Password (optional)</Label>