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.

feat: modernize dashboard, add CLI parity, and harden posting timestamps

jack e28e6abb 1d1f743f

+5139 -554
+1
.gitignore
··· 7 7 npm-debug.log 8 8 .DS_Store 9 9 data/ 10 + web/dist/
+111 -65
README.md
··· 1 1 # Tweets-2-Bsky 2 2 3 - A powerful tool to crosspost Tweets to Bluesky, supporting threads, videos, and high-quality images. 3 + Crosspost Tweets/X posts to Bluesky with support for threads, media, and multiple account mappings. 4 4 5 5 ## Features 6 6 7 - - 🔄 **Crossposting**: Automatically mirrors your Tweets to Bluesky. 8 - - 🧵 **Thread Support**: INTELLIGENTLY handles threads, posting them as Bluesky threads. 9 - - 📹 **Video & GIF Support**: Downloads and uploads videos/GIFs natively to Bluesky (not just links!). 10 - - 🖼️ **High-Quality Images**: Fetches the highest resolution images available. 11 - - 🔗 **Smart Link Expansion**: Resolves `t.co` links to their original URLs. 12 - - 👥 **Multiple Source Accounts**: Map multiple Twitter accounts to a single Bluesky profile. 13 - - ⚙️ **Web Dashboard**: Manage accounts, view status, and trigger runs via a modern UI. 14 - - 🛠️ **CLI & Web Support**: Use the command line or the web interface. 7 + - Automated crossposting from Twitter/X to Bluesky 8 + - Thread-aware posting 9 + - Video/GIF and high-quality image handling 10 + - Multi-source account mappings per Bluesky target 11 + - React + Vite web dashboard (auto light/dark mode) 12 + - Native-styled "Already Posted" feed in dashboard 13 + - Full CLI workflows for CLI-only/cronjob usage 15 14 16 - ## Quick Start 15 + ## Requirements 17 16 18 - 1. **Clone the repository:** 19 - ```bash 20 - git clone https://github.com/yourusername/tweets-2-bsky.git 21 - cd tweets-2-bsky 22 - ``` 17 + - Node.js 22+ 18 + - npm 19 + - Git 23 20 24 - 2. **Install dependencies:** 25 - ```bash 26 - npm install 27 - ``` 21 + ## Fast Setup (Web + CLI) 28 22 29 - 3. **Build the project:** 30 - ```bash 31 - npm run build 32 - ``` 23 + ```bash 24 + git clone https://github.com/j4ckxyz/tweets-2-bsky 25 + cd tweets-2-bsky 26 + npm install 27 + npm run build 28 + npm start 29 + ``` 33 30 34 - 4. **Start the server:** 35 - ```bash 36 - npm start 37 - ``` 38 - Access the dashboard at `http://localhost:3000`. 31 + Open: [http://localhost:3000](http://localhost:3000) 32 + 33 + Notes: 34 + - `npm install` automatically rebuilds native modules (including `better-sqlite3`) for your active Node version. 35 + - If you switch Node versions later, run `npm run rebuild:native`. 36 + 37 + ## CLI-Only Setup 38 + 39 + 1. Configure Twitter cookies: 40 + ```bash 41 + npm run cli -- setup-twitter 42 + ``` 43 + 2. Add mapping(s): 44 + ```bash 45 + npm run cli -- add-mapping 46 + ``` 47 + 3. Run one sync cycle now: 48 + ```bash 49 + npm run cli -- run-now 50 + ``` 39 51 40 52 ## Updating 41 53 42 - To update to the latest version without losing your configuration: 54 + Use: 43 55 44 56 ```bash 45 57 ./update.sh 46 58 ``` 47 59 48 - This script will pull the latest code, install dependencies, and rebuild the project. **Restart your application** after running the update. 60 + What it does: 61 + - pulls latest code 62 + - installs dependencies 63 + - rebuilds native modules 64 + - builds server + web UI 65 + - restarts PM2 process (if PM2 is installed) 66 + - preserves local `config.json` via backup/restore 49 67 50 - ## Configuration & Security 68 + ## CLI Commands (Feature Parity) 51 69 52 - ### Environment Variables 70 + Always use: 53 71 54 - Create a `.env` file for security (optional but recommended): 72 + ```bash 73 + npm run cli -- <command> 74 + ``` 55 75 56 - ```env 57 - PORT=3000 58 - JWT_SECRET=your-super-secret-key-change-this 76 + Core commands: 77 + - `setup-twitter`: Configure primary + backup Twitter cookies 78 + - `setup-ai`: Configure AI provider/API settings 79 + - `add-mapping`, `edit-mapping`, `remove`, `list` 80 + - `set-interval <minutes>`: Scheduler interval 81 + - `run-now [--dry-run] [--web]`: Run one cycle immediately (good for cron) 82 + - `backfill [mapping] --limit 15 [--dry-run] [--web]` 83 + - `import-history [mapping] --limit 15 [--dry-run] [--web]` 84 + - `clear-cache [mapping]` 85 + - `delete-all-posts [mapping]` 86 + - `recent-activity --limit 20` 87 + - `config-export [file]` 88 + - `config-import <file>` 89 + - `status` 90 + 91 + Mapping arguments can be mapping ID, Bluesky handle, or Twitter username. 92 + 93 + ## Cronjob Example 94 + 95 + Run every 5 minutes: 96 + 97 + ```cron 98 + */5 * * * * cd /path/to/tweets-2-bsky && /usr/bin/npm run cli -- run-now >> /tmp/tweets-2-bsky.log 2>&1 59 99 ``` 60 100 61 - > **⚠️ Security Note:** If you do not set `JWT_SECRET`, a fallback secret is used. For production or public-facing deployments, **YOU MUST SET A STRONG SECRET**. 101 + Backfill specific mapping once: 62 102 63 - ### Data Storage 103 + ```bash 104 + npm run cli -- backfill <mapping-id-or-handle> --limit 50 105 + ``` 64 106 65 - - **`config.json`**: Stores your account mappings and encrypted web user passwords. Note that Bluesky app passwords are stored in plain text here to facilitate automated login. **Do not share this file.** 66 - - **`data/database.sqlite`**: Stores the history of processed tweets to prevent duplicates. 107 + ## Web Dashboard 67 108 68 - ## Usage 109 + 1. Register first user (becomes admin) 110 + 2. Configure Twitter + AI settings 111 + 3. Add mappings 112 + 4. Use: 113 + - `Run now` 114 + - backfill/reset actions per mapping 115 + - config export/import 116 + - "Already Posted" feed for native-themed post browsing 69 117 70 - ### Web Interface 118 + ## Configuration & Security 71 119 72 - 1. Register your first account (this user becomes the **Admin**). 73 - 2. Go to settings to configure your Twitter Auth Token and CT0 (cookies). 74 - 3. Add mappings: 75 - * Enter one or more **Twitter Usernames** (comma-separated). 76 - * Enter your **Bluesky Handle** and **App Password**. 77 - 4. The system will check for new tweets every 5 minutes (configurable). 120 + ### Environment variables 78 121 79 - ### CLI 122 + Create `.env` (recommended): 80 123 81 - - **Add Mapping**: `npm run cli add-mapping` 82 - - **Edit Mapping**: `npm run cli edit-mapping` 83 - - **Import History**: `npm run cli import-history` 84 - - **List Accounts**: `npm run cli list` 124 + ```env 125 + PORT=3000 126 + JWT_SECRET=your-super-secret-key-change-this 127 + ``` 85 128 86 - ### 🤖 Gemini AI Alt Text (Optional) 129 + If `JWT_SECRET` is not set, a fallback secret is used. 87 130 88 - This tool can automatically generate Alt Text for images using Google's Gemini AI if the original tweet lacks it. 131 + ### Local data files 89 132 90 - 1. **Get an API Key**: Get a free API Key from [Google AI Studio](https://aistudio.google.com/app/apikey). 91 - 2. **Configure**: 92 - * **Web UI**: Go to the Admin Dashboard and paste your key in the "Gemini AI (Alt Text)" section. 93 - * **Disable**: Leave the field empty to disable this feature (default). 133 + - `config.json`: mappings + auth settings + web users (do not share) 134 + - `data/database.sqlite`: processed tweet history 94 135 95 - ## Twitter Cookies (Auth) 136 + ## Troubleshooting 96 137 97 - You need your Twitter `auth_token` and `ct0` cookies. 98 - 1. Log in to Twitter/X in your browser. 99 - 2. Open Developer Tools (F12) -> Application -> Cookies. 100 - 3. Copy the values for `auth_token` and `ct0`. 138 + See: `TROUBLESHOOTING.md` 139 + 140 + Most common fix after changing Node versions: 141 + 142 + ```bash 143 + npm run rebuild:native 144 + npm run build 145 + npm start 146 + ``` 101 147 102 148 ## License 103 149 104 - MIT 150 + MIT
+33
TROUBLESHOOTING.md
··· 24 24 chmod +x repair_pm2.sh 25 25 ./repair_pm2.sh 26 26 ``` 27 + 28 + ### `better-sqlite3` NODE_MODULE_VERSION mismatch 29 + If startup fails with `ERR_DLOPEN_FAILED` and a `NODE_MODULE_VERSION` mismatch: 30 + 31 + 1. Rebuild native bindings for your active Node version: 32 + ```bash 33 + npm run rebuild:native 34 + ``` 35 + 2. Rebuild and start: 36 + ```bash 37 + npm run build 38 + npm start 39 + ``` 40 + 41 + ### Dashboard appears unstyled / plain text UI 42 + If the app loads but looks mostly unstyled: 43 + 44 + 1. Rebuild web assets: 45 + ```bash 46 + npm run build 47 + ``` 48 + 2. Restart server: 49 + ```bash 50 + npm start 51 + ``` 52 + 3. Hard refresh browser cache (`Cmd+Shift+R` on macOS). 53 + 54 + ### CLI command not recognized 55 + When using npm scripts, pass CLI args after `--`: 56 + 57 + ```bash 58 + npm run cli -- status 59 + ```
+2559 -339
package-lock.json
··· 7 7 "": { 8 8 "name": "tweets-2-bsky", 9 9 "version": "2.0.0", 10 + "hasInstallScript": true, 10 11 "license": "MIT", 11 12 "dependencies": { 12 13 "@atproto/api": "^0.18.9", ··· 16 17 "bcryptjs": "^3.0.3", 17 18 "better-sqlite3": "^12.5.0", 18 19 "cheerio": "^1.1.2", 20 + "class-variance-authority": "^0.7.1", 21 + "clsx": "^2.1.1", 19 22 "commander": "^14.0.2", 20 23 "cors": "^2.8.5", 21 24 "dotenv": "^17.2.3", ··· 24 27 "inquirer": "^13.1.0", 25 28 "iso-639-1": "^3.1.2", 26 29 "jsonwebtoken": "^9.0.3", 30 + "lucide-react": "^0.553.0", 27 31 "node-cron": "^4.2.1", 28 32 "puppeteer-core": "^24.34.0", 29 - "sharp": "^0.34.5" 33 + "react": "^19.2.0", 34 + "react-dom": "^19.2.0", 35 + "sharp": "^0.34.5", 36 + "tailwind-merge": "^3.3.1" 30 37 }, 31 38 "devDependencies": { 32 39 "@biomejs/biome": "^1.9.4", ··· 38 45 "@types/inquirer": "^9.0.9", 39 46 "@types/jsonwebtoken": "^9.0.10", 40 47 "@types/node": "^22.10.2", 48 + "@types/react": "^19.2.6", 49 + "@types/react-dom": "^19.2.3", 41 50 "@types/sharp": "^0.31.1", 51 + "@vitejs/plugin-react": "^5.1.0", 52 + "autoprefixer": "^10.4.21", 53 + "postcss": "^8.5.6", 54 + "tailwindcss": "^3.4.18", 42 55 "tsx": "^4.19.2", 43 - "typescript": "^5.7.2" 56 + "typescript": "^5.7.2", 57 + "vite": "^7.1.12" 58 + } 59 + }, 60 + "node_modules/@alloc/quick-lru": { 61 + "version": "5.2.0", 62 + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", 63 + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", 64 + "dev": true, 65 + "license": "MIT", 66 + "engines": { 67 + "node": ">=10" 68 + }, 69 + "funding": { 70 + "url": "https://github.com/sponsors/sindresorhus" 44 71 } 45 72 }, 46 73 "node_modules/@atproto/api": { 47 - "version": "0.18.10", 48 - "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.18.10.tgz", 49 - "integrity": "sha512-q23wreAGhktrMLepulvljZWHsUOrTIDwhU3gr/uSX3R1TZIZ3i4SxQZVlMqaQHpNJ/5Xj8J1hozkwVpaOX37eA==", 74 + "version": "0.18.21", 75 + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.18.21.tgz", 76 + "integrity": "sha512-s35MIJerGT/pKe2xJtKKswqlIr/ola2r2iURBKBL0Mk1OKe6jP4YvTMh1N2d2PEANFzNNTbKoDaLfJPo2Uvc/w==", 50 77 "license": "MIT", 51 78 "dependencies": { 52 - "@atproto/common-web": "^0.4.10", 53 - "@atproto/lexicon": "^0.6.0", 54 - "@atproto/syntax": "^0.4.2", 79 + "@atproto/common-web": "^0.4.16", 80 + "@atproto/lexicon": "^0.6.1", 81 + "@atproto/syntax": "^0.4.3", 55 82 "@atproto/xrpc": "^0.7.7", 56 83 "await-lock": "^2.2.2", 57 84 "multiformats": "^9.9.0", ··· 60 87 } 61 88 }, 62 89 "node_modules/@atproto/common-web": { 63 - "version": "0.4.10", 64 - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.10.tgz", 65 - "integrity": "sha512-TLDZSgSKzT8ZgOrBrTGK87J1CXve9TEuY6NVVUBRkOMzRRtQzpFb9/ih5WVS/hnaWVvE30CfuyaetRoma+WKNw==", 90 + "version": "0.4.16", 91 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.16.tgz", 92 + "integrity": "sha512-Ufvaff5JgxUyUyTAG0/3o7ltpy3lnZ1DvLjyAnvAf+hHfiK7OMQg+8byr+orN+KP9MtIQaRTsCgYPX+PxMKUoA==", 66 93 "license": "MIT", 67 94 "dependencies": { 68 - "@atproto/lex-data": "0.0.6", 69 - "@atproto/lex-json": "0.0.6", 95 + "@atproto/lex-data": "^0.0.11", 96 + "@atproto/lex-json": "^0.0.11", 97 + "@atproto/syntax": "^0.4.3", 70 98 "zod": "^3.23.8" 71 99 } 72 100 }, 73 101 "node_modules/@atproto/lex-data": { 74 - "version": "0.0.6", 75 - "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.6.tgz", 76 - "integrity": "sha512-MBNB4ghRJQzuXK1zlUPljpPbQcF1LZ5dzxy274KqPt4p3uPuRw0mHjgcCoWzRUNBQC685WMQR4IN9DHtsnG57A==", 102 + "version": "0.0.11", 103 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.11.tgz", 104 + "integrity": "sha512-4+KTtHdqwlhiTKA7D4SACea4jprsNpCQsNALW09wsZ6IHhCDGO5tr1cmV+QnLYe3G3mu1E1yXHXbPUHrUUDT/A==", 77 105 "license": "MIT", 78 106 "dependencies": { 79 - "@atproto/syntax": "0.4.2", 80 107 "multiformats": "^9.9.0", 81 108 "tslib": "^2.8.1", 82 109 "uint8arrays": "3.0.0", ··· 84 111 } 85 112 }, 86 113 "node_modules/@atproto/lex-json": { 87 - "version": "0.0.6", 88 - "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.6.tgz", 89 - "integrity": "sha512-EILnN5cditPvf+PCNjXt7reMuzjugxAL1fpSzmzJbEMGMUwxOf5pPWxRsaA/M3Boip4NQZ+6DVrPOGUMlnqceg==", 114 + "version": "0.0.11", 115 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.11.tgz", 116 + "integrity": "sha512-2IExAoQ4KsR5fyPa1JjIvtR316PvdgRH/l3BVGLBd3cSxM3m5MftIv1B6qZ9HjNiK60SgkWp0mi9574bTNDhBQ==", 90 117 "license": "MIT", 91 118 "dependencies": { 92 - "@atproto/lex-data": "0.0.6", 119 + "@atproto/lex-data": "^0.0.11", 93 120 "tslib": "^2.8.1" 94 121 } 95 122 }, 96 123 "node_modules/@atproto/lexicon": { 97 - "version": "0.6.0", 98 - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.0.tgz", 99 - "integrity": "sha512-5veb8aD+J5M0qszLJ+73KSFsFrJBgAY/nM1TSAJvGY7fNc9ZAT+PSUlmIyrdye9YznAZ07yktalls/TwNV7cHQ==", 124 + "version": "0.6.1", 125 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.1.tgz", 126 + "integrity": "sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw==", 100 127 "license": "MIT", 101 128 "dependencies": { 102 - "@atproto/common-web": "^0.4.7", 103 - "@atproto/syntax": "^0.4.2", 129 + "@atproto/common-web": "^0.4.13", 130 + "@atproto/syntax": "^0.4.3", 104 131 "iso-datestring-validator": "^2.2.2", 105 132 "multiformats": "^9.9.0", 106 133 "zod": "^3.23.8" 107 134 } 108 135 }, 109 136 "node_modules/@atproto/syntax": { 110 - "version": "0.4.2", 111 - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz", 112 - "integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==", 113 - "license": "MIT" 137 + "version": "0.4.3", 138 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.3.tgz", 139 + "integrity": "sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==", 140 + "license": "MIT", 141 + "dependencies": { 142 + "tslib": "^2.8.1" 143 + } 114 144 }, 115 145 "node_modules/@atproto/xrpc": { 116 146 "version": "0.7.7", ··· 122 152 "zod": "^3.23.8" 123 153 } 124 154 }, 155 + "node_modules/@babel/code-frame": { 156 + "version": "7.29.0", 157 + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", 158 + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", 159 + "dev": true, 160 + "license": "MIT", 161 + "dependencies": { 162 + "@babel/helper-validator-identifier": "^7.28.5", 163 + "js-tokens": "^4.0.0", 164 + "picocolors": "^1.1.1" 165 + }, 166 + "engines": { 167 + "node": ">=6.9.0" 168 + } 169 + }, 170 + "node_modules/@babel/compat-data": { 171 + "version": "7.29.0", 172 + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", 173 + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", 174 + "dev": true, 175 + "license": "MIT", 176 + "engines": { 177 + "node": ">=6.9.0" 178 + } 179 + }, 180 + "node_modules/@babel/core": { 181 + "version": "7.29.0", 182 + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", 183 + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", 184 + "dev": true, 185 + "license": "MIT", 186 + "dependencies": { 187 + "@babel/code-frame": "^7.29.0", 188 + "@babel/generator": "^7.29.0", 189 + "@babel/helper-compilation-targets": "^7.28.6", 190 + "@babel/helper-module-transforms": "^7.28.6", 191 + "@babel/helpers": "^7.28.6", 192 + "@babel/parser": "^7.29.0", 193 + "@babel/template": "^7.28.6", 194 + "@babel/traverse": "^7.29.0", 195 + "@babel/types": "^7.29.0", 196 + "@jridgewell/remapping": "^2.3.5", 197 + "convert-source-map": "^2.0.0", 198 + "debug": "^4.1.0", 199 + "gensync": "^1.0.0-beta.2", 200 + "json5": "^2.2.3", 201 + "semver": "^6.3.1" 202 + }, 203 + "engines": { 204 + "node": ">=6.9.0" 205 + }, 206 + "funding": { 207 + "type": "opencollective", 208 + "url": "https://opencollective.com/babel" 209 + } 210 + }, 211 + "node_modules/@babel/generator": { 212 + "version": "7.29.1", 213 + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", 214 + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", 215 + "dev": true, 216 + "license": "MIT", 217 + "dependencies": { 218 + "@babel/parser": "^7.29.0", 219 + "@babel/types": "^7.29.0", 220 + "@jridgewell/gen-mapping": "^0.3.12", 221 + "@jridgewell/trace-mapping": "^0.3.28", 222 + "jsesc": "^3.0.2" 223 + }, 224 + "engines": { 225 + "node": ">=6.9.0" 226 + } 227 + }, 228 + "node_modules/@babel/helper-compilation-targets": { 229 + "version": "7.28.6", 230 + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", 231 + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", 232 + "dev": true, 233 + "license": "MIT", 234 + "dependencies": { 235 + "@babel/compat-data": "^7.28.6", 236 + "@babel/helper-validator-option": "^7.27.1", 237 + "browserslist": "^4.24.0", 238 + "lru-cache": "^5.1.1", 239 + "semver": "^6.3.1" 240 + }, 241 + "engines": { 242 + "node": ">=6.9.0" 243 + } 244 + }, 245 + "node_modules/@babel/helper-globals": { 246 + "version": "7.28.0", 247 + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", 248 + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", 249 + "dev": true, 250 + "license": "MIT", 251 + "engines": { 252 + "node": ">=6.9.0" 253 + } 254 + }, 255 + "node_modules/@babel/helper-module-imports": { 256 + "version": "7.28.6", 257 + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", 258 + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", 259 + "dev": true, 260 + "license": "MIT", 261 + "dependencies": { 262 + "@babel/traverse": "^7.28.6", 263 + "@babel/types": "^7.28.6" 264 + }, 265 + "engines": { 266 + "node": ">=6.9.0" 267 + } 268 + }, 269 + "node_modules/@babel/helper-module-transforms": { 270 + "version": "7.28.6", 271 + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", 272 + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", 273 + "dev": true, 274 + "license": "MIT", 275 + "dependencies": { 276 + "@babel/helper-module-imports": "^7.28.6", 277 + "@babel/helper-validator-identifier": "^7.28.5", 278 + "@babel/traverse": "^7.28.6" 279 + }, 280 + "engines": { 281 + "node": ">=6.9.0" 282 + }, 283 + "peerDependencies": { 284 + "@babel/core": "^7.0.0" 285 + } 286 + }, 287 + "node_modules/@babel/helper-plugin-utils": { 288 + "version": "7.28.6", 289 + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", 290 + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", 291 + "dev": true, 292 + "license": "MIT", 293 + "engines": { 294 + "node": ">=6.9.0" 295 + } 296 + }, 297 + "node_modules/@babel/helper-string-parser": { 298 + "version": "7.27.1", 299 + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", 300 + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", 301 + "dev": true, 302 + "license": "MIT", 303 + "engines": { 304 + "node": ">=6.9.0" 305 + } 306 + }, 307 + "node_modules/@babel/helper-validator-identifier": { 308 + "version": "7.28.5", 309 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", 310 + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", 311 + "dev": true, 312 + "license": "MIT", 313 + "engines": { 314 + "node": ">=6.9.0" 315 + } 316 + }, 317 + "node_modules/@babel/helper-validator-option": { 318 + "version": "7.27.1", 319 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", 320 + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", 321 + "dev": true, 322 + "license": "MIT", 323 + "engines": { 324 + "node": ">=6.9.0" 325 + } 326 + }, 327 + "node_modules/@babel/helpers": { 328 + "version": "7.28.6", 329 + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", 330 + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", 331 + "dev": true, 332 + "license": "MIT", 333 + "dependencies": { 334 + "@babel/template": "^7.28.6", 335 + "@babel/types": "^7.28.6" 336 + }, 337 + "engines": { 338 + "node": ">=6.9.0" 339 + } 340 + }, 341 + "node_modules/@babel/parser": { 342 + "version": "7.29.0", 343 + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", 344 + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", 345 + "dev": true, 346 + "license": "MIT", 347 + "dependencies": { 348 + "@babel/types": "^7.29.0" 349 + }, 350 + "bin": { 351 + "parser": "bin/babel-parser.js" 352 + }, 353 + "engines": { 354 + "node": ">=6.0.0" 355 + } 356 + }, 357 + "node_modules/@babel/plugin-transform-react-jsx-self": { 358 + "version": "7.27.1", 359 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", 360 + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", 361 + "dev": true, 362 + "license": "MIT", 363 + "dependencies": { 364 + "@babel/helper-plugin-utils": "^7.27.1" 365 + }, 366 + "engines": { 367 + "node": ">=6.9.0" 368 + }, 369 + "peerDependencies": { 370 + "@babel/core": "^7.0.0-0" 371 + } 372 + }, 373 + "node_modules/@babel/plugin-transform-react-jsx-source": { 374 + "version": "7.27.1", 375 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", 376 + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", 377 + "dev": true, 378 + "license": "MIT", 379 + "dependencies": { 380 + "@babel/helper-plugin-utils": "^7.27.1" 381 + }, 382 + "engines": { 383 + "node": ">=6.9.0" 384 + }, 385 + "peerDependencies": { 386 + "@babel/core": "^7.0.0-0" 387 + } 388 + }, 389 + "node_modules/@babel/template": { 390 + "version": "7.28.6", 391 + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", 392 + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", 393 + "dev": true, 394 + "license": "MIT", 395 + "dependencies": { 396 + "@babel/code-frame": "^7.28.6", 397 + "@babel/parser": "^7.28.6", 398 + "@babel/types": "^7.28.6" 399 + }, 400 + "engines": { 401 + "node": ">=6.9.0" 402 + } 403 + }, 404 + "node_modules/@babel/traverse": { 405 + "version": "7.29.0", 406 + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", 407 + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", 408 + "dev": true, 409 + "license": "MIT", 410 + "dependencies": { 411 + "@babel/code-frame": "^7.29.0", 412 + "@babel/generator": "^7.29.0", 413 + "@babel/helper-globals": "^7.28.0", 414 + "@babel/parser": "^7.29.0", 415 + "@babel/template": "^7.28.6", 416 + "@babel/types": "^7.29.0", 417 + "debug": "^4.3.1" 418 + }, 419 + "engines": { 420 + "node": ">=6.9.0" 421 + } 422 + }, 423 + "node_modules/@babel/types": { 424 + "version": "7.29.0", 425 + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", 426 + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", 427 + "dev": true, 428 + "license": "MIT", 429 + "dependencies": { 430 + "@babel/helper-string-parser": "^7.27.1", 431 + "@babel/helper-validator-identifier": "^7.28.5" 432 + }, 433 + "engines": { 434 + "node": ">=6.9.0" 435 + } 436 + }, 125 437 "node_modules/@biomejs/biome": { 126 438 "version": "1.9.4", 127 439 "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", ··· 297 609 } 298 610 }, 299 611 "node_modules/@esbuild/aix-ppc64": { 300 - "version": "0.27.2", 301 - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", 302 - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", 612 + "version": "0.27.3", 613 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", 614 + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", 303 615 "cpu": [ 304 616 "ppc64" 305 617 ], ··· 314 626 } 315 627 }, 316 628 "node_modules/@esbuild/android-arm": { 317 - "version": "0.27.2", 318 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", 319 - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", 629 + "version": "0.27.3", 630 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", 631 + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", 320 632 "cpu": [ 321 633 "arm" 322 634 ], ··· 331 643 } 332 644 }, 333 645 "node_modules/@esbuild/android-arm64": { 334 - "version": "0.27.2", 335 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", 336 - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", 646 + "version": "0.27.3", 647 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", 648 + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", 337 649 "cpu": [ 338 650 "arm64" 339 651 ], ··· 348 660 } 349 661 }, 350 662 "node_modules/@esbuild/android-x64": { 351 - "version": "0.27.2", 352 - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", 353 - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", 663 + "version": "0.27.3", 664 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", 665 + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", 354 666 "cpu": [ 355 667 "x64" 356 668 ], ··· 365 677 } 366 678 }, 367 679 "node_modules/@esbuild/darwin-arm64": { 368 - "version": "0.27.2", 369 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", 370 - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", 680 + "version": "0.27.3", 681 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", 682 + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", 371 683 "cpu": [ 372 684 "arm64" 373 685 ], ··· 382 694 } 383 695 }, 384 696 "node_modules/@esbuild/darwin-x64": { 385 - "version": "0.27.2", 386 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", 387 - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", 697 + "version": "0.27.3", 698 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", 699 + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", 388 700 "cpu": [ 389 701 "x64" 390 702 ], ··· 399 711 } 400 712 }, 401 713 "node_modules/@esbuild/freebsd-arm64": { 402 - "version": "0.27.2", 403 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", 404 - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", 714 + "version": "0.27.3", 715 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", 716 + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", 405 717 "cpu": [ 406 718 "arm64" 407 719 ], ··· 416 728 } 417 729 }, 418 730 "node_modules/@esbuild/freebsd-x64": { 419 - "version": "0.27.2", 420 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", 421 - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", 731 + "version": "0.27.3", 732 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", 733 + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", 422 734 "cpu": [ 423 735 "x64" 424 736 ], ··· 433 745 } 434 746 }, 435 747 "node_modules/@esbuild/linux-arm": { 436 - "version": "0.27.2", 437 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", 438 - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", 748 + "version": "0.27.3", 749 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", 750 + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", 439 751 "cpu": [ 440 752 "arm" 441 753 ], ··· 450 762 } 451 763 }, 452 764 "node_modules/@esbuild/linux-arm64": { 453 - "version": "0.27.2", 454 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", 455 - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", 765 + "version": "0.27.3", 766 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", 767 + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", 456 768 "cpu": [ 457 769 "arm64" 458 770 ], ··· 467 779 } 468 780 }, 469 781 "node_modules/@esbuild/linux-ia32": { 470 - "version": "0.27.2", 471 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", 472 - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", 782 + "version": "0.27.3", 783 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", 784 + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", 473 785 "cpu": [ 474 786 "ia32" 475 787 ], ··· 484 796 } 485 797 }, 486 798 "node_modules/@esbuild/linux-loong64": { 487 - "version": "0.27.2", 488 - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", 489 - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", 799 + "version": "0.27.3", 800 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", 801 + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", 490 802 "cpu": [ 491 803 "loong64" 492 804 ], ··· 501 813 } 502 814 }, 503 815 "node_modules/@esbuild/linux-mips64el": { 504 - "version": "0.27.2", 505 - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", 506 - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", 816 + "version": "0.27.3", 817 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", 818 + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", 507 819 "cpu": [ 508 820 "mips64el" 509 821 ], ··· 518 830 } 519 831 }, 520 832 "node_modules/@esbuild/linux-ppc64": { 521 - "version": "0.27.2", 522 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", 523 - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", 833 + "version": "0.27.3", 834 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", 835 + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", 524 836 "cpu": [ 525 837 "ppc64" 526 838 ], ··· 535 847 } 536 848 }, 537 849 "node_modules/@esbuild/linux-riscv64": { 538 - "version": "0.27.2", 539 - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", 540 - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", 850 + "version": "0.27.3", 851 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", 852 + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", 541 853 "cpu": [ 542 854 "riscv64" 543 855 ], ··· 552 864 } 553 865 }, 554 866 "node_modules/@esbuild/linux-s390x": { 555 - "version": "0.27.2", 556 - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", 557 - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", 867 + "version": "0.27.3", 868 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", 869 + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", 558 870 "cpu": [ 559 871 "s390x" 560 872 ], ··· 569 881 } 570 882 }, 571 883 "node_modules/@esbuild/linux-x64": { 572 - "version": "0.27.2", 573 - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", 574 - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", 884 + "version": "0.27.3", 885 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", 886 + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", 575 887 "cpu": [ 576 888 "x64" 577 889 ], ··· 586 898 } 587 899 }, 588 900 "node_modules/@esbuild/netbsd-arm64": { 589 - "version": "0.27.2", 590 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", 591 - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", 901 + "version": "0.27.3", 902 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", 903 + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", 592 904 "cpu": [ 593 905 "arm64" 594 906 ], ··· 603 915 } 604 916 }, 605 917 "node_modules/@esbuild/netbsd-x64": { 606 - "version": "0.27.2", 607 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", 608 - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", 918 + "version": "0.27.3", 919 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", 920 + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", 609 921 "cpu": [ 610 922 "x64" 611 923 ], ··· 620 932 } 621 933 }, 622 934 "node_modules/@esbuild/openbsd-arm64": { 623 - "version": "0.27.2", 624 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", 625 - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", 935 + "version": "0.27.3", 936 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", 937 + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", 626 938 "cpu": [ 627 939 "arm64" 628 940 ], ··· 637 949 } 638 950 }, 639 951 "node_modules/@esbuild/openbsd-x64": { 640 - "version": "0.27.2", 641 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", 642 - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", 952 + "version": "0.27.3", 953 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", 954 + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", 643 955 "cpu": [ 644 956 "x64" 645 957 ], ··· 654 966 } 655 967 }, 656 968 "node_modules/@esbuild/openharmony-arm64": { 657 - "version": "0.27.2", 658 - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", 659 - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", 969 + "version": "0.27.3", 970 + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", 971 + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", 660 972 "cpu": [ 661 973 "arm64" 662 974 ], ··· 671 983 } 672 984 }, 673 985 "node_modules/@esbuild/sunos-x64": { 674 - "version": "0.27.2", 675 - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", 676 - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", 986 + "version": "0.27.3", 987 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", 988 + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", 677 989 "cpu": [ 678 990 "x64" 679 991 ], ··· 688 1000 } 689 1001 }, 690 1002 "node_modules/@esbuild/win32-arm64": { 691 - "version": "0.27.2", 692 - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", 693 - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", 1003 + "version": "0.27.3", 1004 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", 1005 + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", 694 1006 "cpu": [ 695 1007 "arm64" 696 1008 ], ··· 705 1017 } 706 1018 }, 707 1019 "node_modules/@esbuild/win32-ia32": { 708 - "version": "0.27.2", 709 - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", 710 - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", 1020 + "version": "0.27.3", 1021 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", 1022 + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", 711 1023 "cpu": [ 712 1024 "ia32" 713 1025 ], ··· 722 1034 } 723 1035 }, 724 1036 "node_modules/@esbuild/win32-x64": { 725 - "version": "0.27.2", 726 - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", 727 - "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", 1037 + "version": "0.27.3", 1038 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", 1039 + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", 728 1040 "cpu": [ 729 1041 "x64" 730 1042 ], ··· 1213 1525 } 1214 1526 }, 1215 1527 "node_modules/@inquirer/ansi": { 1216 - "version": "2.0.2", 1217 - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.2.tgz", 1218 - "integrity": "sha512-SYLX05PwJVnW+WVegZt1T4Ip1qba1ik+pNJPDiqvk6zS5Y/i8PhRzLpGEtVd7sW0G8cMtkD8t4AZYhQwm8vnww==", 1528 + "version": "2.0.3", 1529 + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.3.tgz", 1530 + "integrity": "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw==", 1219 1531 "license": "MIT", 1220 1532 "engines": { 1221 1533 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" 1222 1534 } 1223 1535 }, 1224 1536 "node_modules/@inquirer/checkbox": { 1225 - "version": "5.0.3", 1226 - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.0.3.tgz", 1227 - "integrity": "sha512-xtQP2eXMFlOcAhZ4ReKP2KZvDIBb1AnCfZ81wWXG3DXLVH0f0g4obE0XDPH+ukAEMRcZT0kdX2AS1jrWGXbpxw==", 1537 + "version": "5.0.4", 1538 + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.0.4.tgz", 1539 + "integrity": "sha512-DrAMU3YBGMUAp6ArwTIp/25CNDtDbxk7UjIrrtM25JVVrlVYlVzHh5HR1BDFu9JMyUoZ4ZanzeaHqNDttf3gVg==", 1228 1540 "license": "MIT", 1229 1541 "dependencies": { 1230 - "@inquirer/ansi": "^2.0.2", 1231 - "@inquirer/core": "^11.1.0", 1232 - "@inquirer/figures": "^2.0.2", 1233 - "@inquirer/type": "^4.0.2" 1542 + "@inquirer/ansi": "^2.0.3", 1543 + "@inquirer/core": "^11.1.1", 1544 + "@inquirer/figures": "^2.0.3", 1545 + "@inquirer/type": "^4.0.3" 1234 1546 }, 1235 1547 "engines": { 1236 1548 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1245 1557 } 1246 1558 }, 1247 1559 "node_modules/@inquirer/confirm": { 1248 - "version": "6.0.3", 1249 - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.3.tgz", 1250 - "integrity": "sha512-lyEvibDFL+NA5R4xl8FUmNhmu81B+LDL9L/MpKkZlQDJZXzG8InxiqYxiAlQYa9cqLLhYqKLQwZqXmSTqCLjyw==", 1560 + "version": "6.0.4", 1561 + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.4.tgz", 1562 + "integrity": "sha512-WdaPe7foUnoGYvXzH4jp4wH/3l+dBhZ3uwhKjXjwdrq5tEIFaANxj6zrGHxLdsIA0yKM0kFPVcEalOZXBB5ISA==", 1251 1563 "license": "MIT", 1252 1564 "dependencies": { 1253 - "@inquirer/core": "^11.1.0", 1254 - "@inquirer/type": "^4.0.2" 1565 + "@inquirer/core": "^11.1.1", 1566 + "@inquirer/type": "^4.0.3" 1255 1567 }, 1256 1568 "engines": { 1257 1569 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1266 1578 } 1267 1579 }, 1268 1580 "node_modules/@inquirer/core": { 1269 - "version": "11.1.0", 1270 - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.0.tgz", 1271 - "integrity": "sha512-+jD/34T1pK8M5QmZD/ENhOfXdl9Zr+BrQAUc5h2anWgi7gggRq15ZbiBeLoObj0TLbdgW7TAIQRU2boMc9uOKQ==", 1581 + "version": "11.1.1", 1582 + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.1.tgz", 1583 + "integrity": "sha512-hV9o15UxX46OyQAtaoMqAOxGR8RVl1aZtDx1jHbCtSJy1tBdTfKxLPKf7utsE4cRy4tcmCQ4+vdV+ca+oNxqNA==", 1272 1584 "license": "MIT", 1273 1585 "dependencies": { 1274 - "@inquirer/ansi": "^2.0.2", 1275 - "@inquirer/figures": "^2.0.2", 1276 - "@inquirer/type": "^4.0.2", 1586 + "@inquirer/ansi": "^2.0.3", 1587 + "@inquirer/figures": "^2.0.3", 1588 + "@inquirer/type": "^4.0.3", 1277 1589 "cli-width": "^4.1.0", 1278 1590 "mute-stream": "^3.0.0", 1279 1591 "signal-exit": "^4.1.0", ··· 1292 1604 } 1293 1605 }, 1294 1606 "node_modules/@inquirer/editor": { 1295 - "version": "5.0.3", 1296 - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.0.3.tgz", 1297 - "integrity": "sha512-wYyQo96TsAqIciP/r5D3cFeV8h4WqKQ/YOvTg5yOfP2sqEbVVpbxPpfV3LM5D0EP4zUI3EZVHyIUIllnoIa8OQ==", 1607 + "version": "5.0.4", 1608 + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.0.4.tgz", 1609 + "integrity": "sha512-QI3Jfqcv6UO2/VJaEFONH8Im1ll++Xn/AJTBn9Xf+qx2M+H8KZAdQ5sAe2vtYlo+mLW+d7JaMJB4qWtK4BG3pw==", 1298 1610 "license": "MIT", 1299 1611 "dependencies": { 1300 - "@inquirer/core": "^11.1.0", 1301 - "@inquirer/external-editor": "^2.0.2", 1302 - "@inquirer/type": "^4.0.2" 1612 + "@inquirer/core": "^11.1.1", 1613 + "@inquirer/external-editor": "^2.0.3", 1614 + "@inquirer/type": "^4.0.3" 1303 1615 }, 1304 1616 "engines": { 1305 1617 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1314 1626 } 1315 1627 }, 1316 1628 "node_modules/@inquirer/expand": { 1317 - "version": "5.0.3", 1318 - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.0.3.tgz", 1319 - "integrity": "sha512-2oINvuL27ujjxd95f6K2K909uZOU2x1WiAl7Wb1X/xOtL8CgQ1kSxzykIr7u4xTkXkXOAkCuF45T588/YKee7w==", 1629 + "version": "5.0.4", 1630 + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.0.4.tgz", 1631 + "integrity": "sha512-0I/16YwPPP0Co7a5MsomlZLpch48NzYfToyqYAOWtBmaXSB80RiNQ1J+0xx2eG+Wfxt0nHtpEWSRr6CzNVnOGg==", 1320 1632 "license": "MIT", 1321 1633 "dependencies": { 1322 - "@inquirer/core": "^11.1.0", 1323 - "@inquirer/type": "^4.0.2" 1634 + "@inquirer/core": "^11.1.1", 1635 + "@inquirer/type": "^4.0.3" 1324 1636 }, 1325 1637 "engines": { 1326 1638 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1335 1647 } 1336 1648 }, 1337 1649 "node_modules/@inquirer/external-editor": { 1338 - "version": "2.0.2", 1339 - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-2.0.2.tgz", 1340 - "integrity": "sha512-X/fMXK7vXomRWEex1j8mnj7s1mpnTeP4CO/h2gysJhHLT2WjBnLv4ZQEGpm/kcYI8QfLZ2fgW+9kTKD+jeopLg==", 1650 + "version": "2.0.3", 1651 + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-2.0.3.tgz", 1652 + "integrity": "sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w==", 1341 1653 "license": "MIT", 1342 1654 "dependencies": { 1343 1655 "chardet": "^2.1.1", 1344 - "iconv-lite": "^0.7.0" 1656 + "iconv-lite": "^0.7.2" 1345 1657 }, 1346 1658 "engines": { 1347 1659 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1356 1668 } 1357 1669 }, 1358 1670 "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { 1359 - "version": "0.7.1", 1360 - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", 1361 - "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", 1671 + "version": "0.7.2", 1672 + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", 1673 + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", 1362 1674 "license": "MIT", 1363 1675 "dependencies": { 1364 1676 "safer-buffer": ">= 2.1.2 < 3.0.0" ··· 1372 1684 } 1373 1685 }, 1374 1686 "node_modules/@inquirer/figures": { 1375 - "version": "2.0.2", 1376 - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.2.tgz", 1377 - "integrity": "sha512-qXm6EVvQx/FmnSrCWCIGtMHwqeLgxABP8XgcaAoywsL0NFga9gD5kfG0gXiv80GjK9Hsoz4pgGwF/+CjygyV9A==", 1687 + "version": "2.0.3", 1688 + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.3.tgz", 1689 + "integrity": "sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g==", 1378 1690 "license": "MIT", 1379 1691 "engines": { 1380 1692 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" 1381 1693 } 1382 1694 }, 1383 1695 "node_modules/@inquirer/input": { 1384 - "version": "5.0.3", 1385 - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.0.3.tgz", 1386 - "integrity": "sha512-4R0TdWl53dtp79Vs6Df2OHAtA2FVNqya1hND1f5wjHWxZJxwDMSNB1X5ADZJSsQKYAJ5JHCTO+GpJZ42mK0Otw==", 1696 + "version": "5.0.4", 1697 + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.0.4.tgz", 1698 + "integrity": "sha512-4B3s3jvTREDFvXWit92Yc6jF1RJMDy2VpSqKtm4We2oVU65YOh2szY5/G14h4fHlyQdpUmazU5MPCFZPRJ0AOw==", 1387 1699 "license": "MIT", 1388 1700 "dependencies": { 1389 - "@inquirer/core": "^11.1.0", 1390 - "@inquirer/type": "^4.0.2" 1701 + "@inquirer/core": "^11.1.1", 1702 + "@inquirer/type": "^4.0.3" 1391 1703 }, 1392 1704 "engines": { 1393 1705 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1402 1714 } 1403 1715 }, 1404 1716 "node_modules/@inquirer/number": { 1405 - "version": "4.0.3", 1406 - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.0.3.tgz", 1407 - "integrity": "sha512-TjQLe93GGo5snRlu83JxE38ZPqj5ZVggL+QqqAF2oBA5JOJoxx25GG3EGH/XN/Os5WOmKfO8iLVdCXQxXRZIMQ==", 1717 + "version": "4.0.4", 1718 + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.0.4.tgz", 1719 + "integrity": "sha512-CmMp9LF5HwE+G/xWsC333TlCzYYbXMkcADkKzcawh49fg2a1ryLc7JL1NJYYt1lJ+8f4slikNjJM9TEL/AljYQ==", 1408 1720 "license": "MIT", 1409 1721 "dependencies": { 1410 - "@inquirer/core": "^11.1.0", 1411 - "@inquirer/type": "^4.0.2" 1722 + "@inquirer/core": "^11.1.1", 1723 + "@inquirer/type": "^4.0.3" 1412 1724 }, 1413 1725 "engines": { 1414 1726 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1423 1735 } 1424 1736 }, 1425 1737 "node_modules/@inquirer/password": { 1426 - "version": "5.0.3", 1427 - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.0.3.tgz", 1428 - "integrity": "sha512-rCozGbUMAHedTeYWEN8sgZH4lRCdgG/WinFkit6ZPsp8JaNg2T0g3QslPBS5XbpORyKP/I+xyBO81kFEvhBmjA==", 1738 + "version": "5.0.4", 1739 + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.0.4.tgz", 1740 + "integrity": "sha512-ZCEPyVYvHK4W4p2Gy6sTp9nqsdHQCfiPXIP9LbJVW4yCinnxL/dDDmPaEZVysGrj8vxVReRnpfS2fOeODe9zjg==", 1429 1741 "license": "MIT", 1430 1742 "dependencies": { 1431 - "@inquirer/ansi": "^2.0.2", 1432 - "@inquirer/core": "^11.1.0", 1433 - "@inquirer/type": "^4.0.2" 1743 + "@inquirer/ansi": "^2.0.3", 1744 + "@inquirer/core": "^11.1.1", 1745 + "@inquirer/type": "^4.0.3" 1434 1746 }, 1435 1747 "engines": { 1436 1748 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1445 1757 } 1446 1758 }, 1447 1759 "node_modules/@inquirer/prompts": { 1448 - "version": "8.1.0", 1449 - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.1.0.tgz", 1450 - "integrity": "sha512-LsZMdKcmRNF5LyTRuZE5nWeOjganzmN3zwbtNfcs6GPh3I2TsTtF1UYZlbxVfhxd+EuUqLGs/Lm3Xt4v6Az1wA==", 1760 + "version": "8.2.0", 1761 + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.2.0.tgz", 1762 + "integrity": "sha512-rqTzOprAj55a27jctS3vhvDDJzYXsr33WXTjODgVOru21NvBo9yIgLIAf7SBdSV0WERVly3dR6TWyp7ZHkvKFA==", 1451 1763 "license": "MIT", 1452 1764 "dependencies": { 1453 - "@inquirer/checkbox": "^5.0.3", 1454 - "@inquirer/confirm": "^6.0.3", 1455 - "@inquirer/editor": "^5.0.3", 1456 - "@inquirer/expand": "^5.0.3", 1457 - "@inquirer/input": "^5.0.3", 1458 - "@inquirer/number": "^4.0.3", 1459 - "@inquirer/password": "^5.0.3", 1460 - "@inquirer/rawlist": "^5.1.0", 1461 - "@inquirer/search": "^4.0.3", 1462 - "@inquirer/select": "^5.0.3" 1765 + "@inquirer/checkbox": "^5.0.4", 1766 + "@inquirer/confirm": "^6.0.4", 1767 + "@inquirer/editor": "^5.0.4", 1768 + "@inquirer/expand": "^5.0.4", 1769 + "@inquirer/input": "^5.0.4", 1770 + "@inquirer/number": "^4.0.4", 1771 + "@inquirer/password": "^5.0.4", 1772 + "@inquirer/rawlist": "^5.2.0", 1773 + "@inquirer/search": "^4.1.0", 1774 + "@inquirer/select": "^5.0.4" 1463 1775 }, 1464 1776 "engines": { 1465 1777 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1474 1786 } 1475 1787 }, 1476 1788 "node_modules/@inquirer/rawlist": { 1477 - "version": "5.1.0", 1478 - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.1.0.tgz", 1479 - "integrity": "sha512-yUCuVh0jW026Gr2tZlG3kHignxcrLKDR3KBp+eUgNz+BAdSeZk0e18yt2gyBr+giYhj/WSIHCmPDOgp1mT2niQ==", 1789 + "version": "5.2.0", 1790 + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.2.0.tgz", 1791 + "integrity": "sha512-CciqGoOUMrFo6HxvOtU5uL8fkjCmzyeB6fG7O1vdVAZVSopUBYECOwevDBlqNLyyYmzpm2Gsn/7nLrpruy9RFg==", 1480 1792 "license": "MIT", 1481 1793 "dependencies": { 1482 - "@inquirer/core": "^11.1.0", 1483 - "@inquirer/type": "^4.0.2" 1794 + "@inquirer/core": "^11.1.1", 1795 + "@inquirer/type": "^4.0.3" 1484 1796 }, 1485 1797 "engines": { 1486 1798 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1495 1807 } 1496 1808 }, 1497 1809 "node_modules/@inquirer/search": { 1498 - "version": "4.0.3", 1499 - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.0.3.tgz", 1500 - "integrity": "sha512-lzqVw0YwuKYetk5VwJ81Ba+dyVlhseHPx9YnRKQgwXdFS0kEavCz2gngnNhnMIxg8+j1N/rUl1t5s1npwa7bqg==", 1810 + "version": "4.1.0", 1811 + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.1.0.tgz", 1812 + "integrity": "sha512-EAzemfiP4IFvIuWnrHpgZs9lAhWDA0GM3l9F4t4mTQ22IFtzfrk8xbkMLcAN7gmVML9O/i+Hzu8yOUyAaL6BKA==", 1501 1813 "license": "MIT", 1502 1814 "dependencies": { 1503 - "@inquirer/core": "^11.1.0", 1504 - "@inquirer/figures": "^2.0.2", 1505 - "@inquirer/type": "^4.0.2" 1815 + "@inquirer/core": "^11.1.1", 1816 + "@inquirer/figures": "^2.0.3", 1817 + "@inquirer/type": "^4.0.3" 1506 1818 }, 1507 1819 "engines": { 1508 1820 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1517 1829 } 1518 1830 }, 1519 1831 "node_modules/@inquirer/select": { 1520 - "version": "5.0.3", 1521 - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.0.3.tgz", 1522 - "integrity": "sha512-M+ynbwS0ecQFDYMFrQrybA0qL8DV0snpc4kKevCCNaTpfghsRowRY7SlQBeIYNzHqXtiiz4RG9vTOeb/udew7w==", 1832 + "version": "5.0.4", 1833 + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.0.4.tgz", 1834 + "integrity": "sha512-s8KoGpPYMEQ6WXc0dT9blX2NtIulMdLOO3LA1UKOiv7KFWzlJ6eLkEYTDBIi+JkyKXyn8t/CD6TinxGjyLt57g==", 1523 1835 "license": "MIT", 1524 1836 "dependencies": { 1525 - "@inquirer/ansi": "^2.0.2", 1526 - "@inquirer/core": "^11.1.0", 1527 - "@inquirer/figures": "^2.0.2", 1528 - "@inquirer/type": "^4.0.2" 1837 + "@inquirer/ansi": "^2.0.3", 1838 + "@inquirer/core": "^11.1.1", 1839 + "@inquirer/figures": "^2.0.3", 1840 + "@inquirer/type": "^4.0.3" 1529 1841 }, 1530 1842 "engines": { 1531 1843 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1540 1852 } 1541 1853 }, 1542 1854 "node_modules/@inquirer/type": { 1543 - "version": "4.0.2", 1544 - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.2.tgz", 1545 - "integrity": "sha512-cae7mzluplsjSdgFA6ACLygb5jC8alO0UUnFPyu0E7tNRPrL+q/f8VcSXp+cjZQ7l5CMpDpi2G1+IQvkOiL1Lw==", 1855 + "version": "4.0.3", 1856 + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.3.tgz", 1857 + "integrity": "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw==", 1546 1858 "license": "MIT", 1547 1859 "engines": { 1548 1860 "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" ··· 1556 1868 } 1557 1869 } 1558 1870 }, 1871 + "node_modules/@jridgewell/gen-mapping": { 1872 + "version": "0.3.13", 1873 + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", 1874 + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", 1875 + "dev": true, 1876 + "license": "MIT", 1877 + "dependencies": { 1878 + "@jridgewell/sourcemap-codec": "^1.5.0", 1879 + "@jridgewell/trace-mapping": "^0.3.24" 1880 + } 1881 + }, 1882 + "node_modules/@jridgewell/remapping": { 1883 + "version": "2.3.5", 1884 + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", 1885 + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", 1886 + "dev": true, 1887 + "license": "MIT", 1888 + "dependencies": { 1889 + "@jridgewell/gen-mapping": "^0.3.5", 1890 + "@jridgewell/trace-mapping": "^0.3.24" 1891 + } 1892 + }, 1893 + "node_modules/@jridgewell/resolve-uri": { 1894 + "version": "3.1.2", 1895 + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 1896 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 1897 + "dev": true, 1898 + "license": "MIT", 1899 + "engines": { 1900 + "node": ">=6.0.0" 1901 + } 1902 + }, 1903 + "node_modules/@jridgewell/sourcemap-codec": { 1904 + "version": "1.5.5", 1905 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", 1906 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", 1907 + "dev": true, 1908 + "license": "MIT" 1909 + }, 1910 + "node_modules/@jridgewell/trace-mapping": { 1911 + "version": "0.3.31", 1912 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", 1913 + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", 1914 + "dev": true, 1915 + "license": "MIT", 1916 + "dependencies": { 1917 + "@jridgewell/resolve-uri": "^3.1.0", 1918 + "@jridgewell/sourcemap-codec": "^1.4.14" 1919 + } 1920 + }, 1559 1921 "node_modules/@noble/hashes": { 1560 - "version": "1.8.0", 1561 - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", 1562 - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", 1922 + "version": "2.0.1", 1923 + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", 1924 + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", 1563 1925 "license": "MIT", 1564 1926 "engines": { 1565 - "node": "^14.21.3 || >=16" 1927 + "node": ">= 20.19.0" 1566 1928 }, 1567 1929 "funding": { 1568 1930 "url": "https://paulmillr.com/funding/" 1569 1931 } 1570 1932 }, 1933 + "node_modules/@nodelib/fs.scandir": { 1934 + "version": "2.1.5", 1935 + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 1936 + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 1937 + "dev": true, 1938 + "license": "MIT", 1939 + "dependencies": { 1940 + "@nodelib/fs.stat": "2.0.5", 1941 + "run-parallel": "^1.1.9" 1942 + }, 1943 + "engines": { 1944 + "node": ">= 8" 1945 + } 1946 + }, 1947 + "node_modules/@nodelib/fs.stat": { 1948 + "version": "2.0.5", 1949 + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 1950 + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 1951 + "dev": true, 1952 + "license": "MIT", 1953 + "engines": { 1954 + "node": ">= 8" 1955 + } 1956 + }, 1957 + "node_modules/@nodelib/fs.walk": { 1958 + "version": "1.2.8", 1959 + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 1960 + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 1961 + "dev": true, 1962 + "license": "MIT", 1963 + "dependencies": { 1964 + "@nodelib/fs.scandir": "2.1.5", 1965 + "fastq": "^1.6.0" 1966 + }, 1967 + "engines": { 1968 + "node": ">= 8" 1969 + } 1970 + }, 1571 1971 "node_modules/@puppeteer/browsers": { 1572 - "version": "2.11.0", 1573 - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.11.0.tgz", 1574 - "integrity": "sha512-n6oQX6mYkG8TRPuPXmbPidkUbsSRalhmaaVAQxvH1IkQy63cwsH+kOjB3e4cpCDHg0aSvsiX9bQ4s2VB6mGWUQ==", 1972 + "version": "2.12.1", 1973 + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.12.1.tgz", 1974 + "integrity": "sha512-fXa6uXLxfslBlus3MEpW8S6S9fe5RwmAE5Gd8u3krqOwnkZJV3/lQJiY3LaFdTctLLqJtyMgEUGkbDnRNf6vbQ==", 1575 1975 "license": "Apache-2.0", 1576 1976 "dependencies": { 1577 1977 "debug": "^4.4.3", 1578 1978 "extract-zip": "^2.0.1", 1579 1979 "progress": "^2.0.3", 1580 1980 "proxy-agent": "^6.5.0", 1581 - "semver": "^7.7.3", 1981 + "semver": "^7.7.4", 1582 1982 "tar-fs": "^3.1.1", 1583 1983 "yargs": "^17.7.2" 1584 1984 }, ··· 1589 1989 "node": ">=18" 1590 1990 } 1591 1991 }, 1992 + "node_modules/@puppeteer/browsers/node_modules/semver": { 1993 + "version": "7.7.4", 1994 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", 1995 + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", 1996 + "license": "ISC", 1997 + "bin": { 1998 + "semver": "bin/semver.js" 1999 + }, 2000 + "engines": { 2001 + "node": ">=10" 2002 + } 2003 + }, 1592 2004 "node_modules/@puppeteer/browsers/node_modules/tar-fs": { 1593 2005 "version": "3.1.1", 1594 2006 "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", ··· 1614 2026 "streamx": "^2.15.0" 1615 2027 } 1616 2028 }, 2029 + "node_modules/@rolldown/pluginutils": { 2030 + "version": "1.0.0-rc.3", 2031 + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", 2032 + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", 2033 + "dev": true, 2034 + "license": "MIT" 2035 + }, 2036 + "node_modules/@rollup/rollup-android-arm-eabi": { 2037 + "version": "4.57.1", 2038 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", 2039 + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", 2040 + "cpu": [ 2041 + "arm" 2042 + ], 2043 + "dev": true, 2044 + "license": "MIT", 2045 + "optional": true, 2046 + "os": [ 2047 + "android" 2048 + ] 2049 + }, 2050 + "node_modules/@rollup/rollup-android-arm64": { 2051 + "version": "4.57.1", 2052 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", 2053 + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", 2054 + "cpu": [ 2055 + "arm64" 2056 + ], 2057 + "dev": true, 2058 + "license": "MIT", 2059 + "optional": true, 2060 + "os": [ 2061 + "android" 2062 + ] 2063 + }, 2064 + "node_modules/@rollup/rollup-darwin-arm64": { 2065 + "version": "4.57.1", 2066 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", 2067 + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", 2068 + "cpu": [ 2069 + "arm64" 2070 + ], 2071 + "dev": true, 2072 + "license": "MIT", 2073 + "optional": true, 2074 + "os": [ 2075 + "darwin" 2076 + ] 2077 + }, 2078 + "node_modules/@rollup/rollup-darwin-x64": { 2079 + "version": "4.57.1", 2080 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", 2081 + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", 2082 + "cpu": [ 2083 + "x64" 2084 + ], 2085 + "dev": true, 2086 + "license": "MIT", 2087 + "optional": true, 2088 + "os": [ 2089 + "darwin" 2090 + ] 2091 + }, 2092 + "node_modules/@rollup/rollup-freebsd-arm64": { 2093 + "version": "4.57.1", 2094 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", 2095 + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", 2096 + "cpu": [ 2097 + "arm64" 2098 + ], 2099 + "dev": true, 2100 + "license": "MIT", 2101 + "optional": true, 2102 + "os": [ 2103 + "freebsd" 2104 + ] 2105 + }, 2106 + "node_modules/@rollup/rollup-freebsd-x64": { 2107 + "version": "4.57.1", 2108 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", 2109 + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", 2110 + "cpu": [ 2111 + "x64" 2112 + ], 2113 + "dev": true, 2114 + "license": "MIT", 2115 + "optional": true, 2116 + "os": [ 2117 + "freebsd" 2118 + ] 2119 + }, 2120 + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 2121 + "version": "4.57.1", 2122 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", 2123 + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", 2124 + "cpu": [ 2125 + "arm" 2126 + ], 2127 + "dev": true, 2128 + "license": "MIT", 2129 + "optional": true, 2130 + "os": [ 2131 + "linux" 2132 + ] 2133 + }, 2134 + "node_modules/@rollup/rollup-linux-arm-musleabihf": { 2135 + "version": "4.57.1", 2136 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", 2137 + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", 2138 + "cpu": [ 2139 + "arm" 2140 + ], 2141 + "dev": true, 2142 + "license": "MIT", 2143 + "optional": true, 2144 + "os": [ 2145 + "linux" 2146 + ] 2147 + }, 2148 + "node_modules/@rollup/rollup-linux-arm64-gnu": { 2149 + "version": "4.57.1", 2150 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", 2151 + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", 2152 + "cpu": [ 2153 + "arm64" 2154 + ], 2155 + "dev": true, 2156 + "license": "MIT", 2157 + "optional": true, 2158 + "os": [ 2159 + "linux" 2160 + ] 2161 + }, 2162 + "node_modules/@rollup/rollup-linux-arm64-musl": { 2163 + "version": "4.57.1", 2164 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", 2165 + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", 2166 + "cpu": [ 2167 + "arm64" 2168 + ], 2169 + "dev": true, 2170 + "license": "MIT", 2171 + "optional": true, 2172 + "os": [ 2173 + "linux" 2174 + ] 2175 + }, 2176 + "node_modules/@rollup/rollup-linux-loong64-gnu": { 2177 + "version": "4.57.1", 2178 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", 2179 + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", 2180 + "cpu": [ 2181 + "loong64" 2182 + ], 2183 + "dev": true, 2184 + "license": "MIT", 2185 + "optional": true, 2186 + "os": [ 2187 + "linux" 2188 + ] 2189 + }, 2190 + "node_modules/@rollup/rollup-linux-loong64-musl": { 2191 + "version": "4.57.1", 2192 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", 2193 + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", 2194 + "cpu": [ 2195 + "loong64" 2196 + ], 2197 + "dev": true, 2198 + "license": "MIT", 2199 + "optional": true, 2200 + "os": [ 2201 + "linux" 2202 + ] 2203 + }, 2204 + "node_modules/@rollup/rollup-linux-ppc64-gnu": { 2205 + "version": "4.57.1", 2206 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", 2207 + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", 2208 + "cpu": [ 2209 + "ppc64" 2210 + ], 2211 + "dev": true, 2212 + "license": "MIT", 2213 + "optional": true, 2214 + "os": [ 2215 + "linux" 2216 + ] 2217 + }, 2218 + "node_modules/@rollup/rollup-linux-ppc64-musl": { 2219 + "version": "4.57.1", 2220 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", 2221 + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", 2222 + "cpu": [ 2223 + "ppc64" 2224 + ], 2225 + "dev": true, 2226 + "license": "MIT", 2227 + "optional": true, 2228 + "os": [ 2229 + "linux" 2230 + ] 2231 + }, 2232 + "node_modules/@rollup/rollup-linux-riscv64-gnu": { 2233 + "version": "4.57.1", 2234 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", 2235 + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", 2236 + "cpu": [ 2237 + "riscv64" 2238 + ], 2239 + "dev": true, 2240 + "license": "MIT", 2241 + "optional": true, 2242 + "os": [ 2243 + "linux" 2244 + ] 2245 + }, 2246 + "node_modules/@rollup/rollup-linux-riscv64-musl": { 2247 + "version": "4.57.1", 2248 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", 2249 + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", 2250 + "cpu": [ 2251 + "riscv64" 2252 + ], 2253 + "dev": true, 2254 + "license": "MIT", 2255 + "optional": true, 2256 + "os": [ 2257 + "linux" 2258 + ] 2259 + }, 2260 + "node_modules/@rollup/rollup-linux-s390x-gnu": { 2261 + "version": "4.57.1", 2262 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", 2263 + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", 2264 + "cpu": [ 2265 + "s390x" 2266 + ], 2267 + "dev": true, 2268 + "license": "MIT", 2269 + "optional": true, 2270 + "os": [ 2271 + "linux" 2272 + ] 2273 + }, 2274 + "node_modules/@rollup/rollup-linux-x64-gnu": { 2275 + "version": "4.57.1", 2276 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", 2277 + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", 2278 + "cpu": [ 2279 + "x64" 2280 + ], 2281 + "dev": true, 2282 + "license": "MIT", 2283 + "optional": true, 2284 + "os": [ 2285 + "linux" 2286 + ] 2287 + }, 2288 + "node_modules/@rollup/rollup-linux-x64-musl": { 2289 + "version": "4.57.1", 2290 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", 2291 + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", 2292 + "cpu": [ 2293 + "x64" 2294 + ], 2295 + "dev": true, 2296 + "license": "MIT", 2297 + "optional": true, 2298 + "os": [ 2299 + "linux" 2300 + ] 2301 + }, 2302 + "node_modules/@rollup/rollup-openbsd-x64": { 2303 + "version": "4.57.1", 2304 + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", 2305 + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", 2306 + "cpu": [ 2307 + "x64" 2308 + ], 2309 + "dev": true, 2310 + "license": "MIT", 2311 + "optional": true, 2312 + "os": [ 2313 + "openbsd" 2314 + ] 2315 + }, 2316 + "node_modules/@rollup/rollup-openharmony-arm64": { 2317 + "version": "4.57.1", 2318 + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", 2319 + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", 2320 + "cpu": [ 2321 + "arm64" 2322 + ], 2323 + "dev": true, 2324 + "license": "MIT", 2325 + "optional": true, 2326 + "os": [ 2327 + "openharmony" 2328 + ] 2329 + }, 2330 + "node_modules/@rollup/rollup-win32-arm64-msvc": { 2331 + "version": "4.57.1", 2332 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", 2333 + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", 2334 + "cpu": [ 2335 + "arm64" 2336 + ], 2337 + "dev": true, 2338 + "license": "MIT", 2339 + "optional": true, 2340 + "os": [ 2341 + "win32" 2342 + ] 2343 + }, 2344 + "node_modules/@rollup/rollup-win32-ia32-msvc": { 2345 + "version": "4.57.1", 2346 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", 2347 + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", 2348 + "cpu": [ 2349 + "ia32" 2350 + ], 2351 + "dev": true, 2352 + "license": "MIT", 2353 + "optional": true, 2354 + "os": [ 2355 + "win32" 2356 + ] 2357 + }, 2358 + "node_modules/@rollup/rollup-win32-x64-gnu": { 2359 + "version": "4.57.1", 2360 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", 2361 + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", 2362 + "cpu": [ 2363 + "x64" 2364 + ], 2365 + "dev": true, 2366 + "license": "MIT", 2367 + "optional": true, 2368 + "os": [ 2369 + "win32" 2370 + ] 2371 + }, 2372 + "node_modules/@rollup/rollup-win32-x64-msvc": { 2373 + "version": "4.57.1", 2374 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", 2375 + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", 2376 + "cpu": [ 2377 + "x64" 2378 + ], 2379 + "dev": true, 2380 + "license": "MIT", 2381 + "optional": true, 2382 + "os": [ 2383 + "win32" 2384 + ] 2385 + }, 1617 2386 "node_modules/@sinclair/typebox": { 1618 2387 "version": "0.32.35", 1619 2388 "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.32.35.tgz", ··· 1656 2425 "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", 1657 2426 "license": "MIT" 1658 2427 }, 2428 + "node_modules/@types/babel__core": { 2429 + "version": "7.20.5", 2430 + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", 2431 + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", 2432 + "dev": true, 2433 + "license": "MIT", 2434 + "dependencies": { 2435 + "@babel/parser": "^7.20.7", 2436 + "@babel/types": "^7.20.7", 2437 + "@types/babel__generator": "*", 2438 + "@types/babel__template": "*", 2439 + "@types/babel__traverse": "*" 2440 + } 2441 + }, 2442 + "node_modules/@types/babel__generator": { 2443 + "version": "7.27.0", 2444 + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", 2445 + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", 2446 + "dev": true, 2447 + "license": "MIT", 2448 + "dependencies": { 2449 + "@babel/types": "^7.0.0" 2450 + } 2451 + }, 2452 + "node_modules/@types/babel__template": { 2453 + "version": "7.4.4", 2454 + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", 2455 + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", 2456 + "dev": true, 2457 + "license": "MIT", 2458 + "dependencies": { 2459 + "@babel/parser": "^7.1.0", 2460 + "@babel/types": "^7.0.0" 2461 + } 2462 + }, 2463 + "node_modules/@types/babel__traverse": { 2464 + "version": "7.28.0", 2465 + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", 2466 + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", 2467 + "dev": true, 2468 + "license": "MIT", 2469 + "dependencies": { 2470 + "@babel/types": "^7.28.2" 2471 + } 2472 + }, 1659 2473 "node_modules/@types/bcryptjs": { 1660 2474 "version": "2.4.6", 1661 2475 "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", ··· 1714 2528 "@types/node": "*" 1715 2529 } 1716 2530 }, 2531 + "node_modules/@types/estree": { 2532 + "version": "1.0.8", 2533 + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", 2534 + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", 2535 + "dev": true, 2536 + "license": "MIT" 2537 + }, 1717 2538 "node_modules/@types/express": { 1718 2539 "version": "5.0.6", 1719 2540 "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", ··· 1727 2548 } 1728 2549 }, 1729 2550 "node_modules/@types/express-serve-static-core": { 1730 - "version": "5.1.0", 1731 - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", 1732 - "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", 2551 + "version": "5.1.1", 2552 + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", 2553 + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", 1733 2554 "dev": true, 1734 2555 "license": "MIT", 1735 2556 "dependencies": { ··· 1776 2597 "license": "MIT" 1777 2598 }, 1778 2599 "node_modules/@types/node": { 1779 - "version": "22.19.3", 1780 - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", 1781 - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", 2600 + "version": "22.19.11", 2601 + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", 2602 + "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", 1782 2603 "devOptional": true, 1783 2604 "license": "MIT", 1784 2605 "dependencies": { ··· 1799 2620 "dev": true, 1800 2621 "license": "MIT" 1801 2622 }, 2623 + "node_modules/@types/react": { 2624 + "version": "19.2.14", 2625 + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", 2626 + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", 2627 + "dev": true, 2628 + "license": "MIT", 2629 + "dependencies": { 2630 + "csstype": "^3.2.2" 2631 + } 2632 + }, 2633 + "node_modules/@types/react-dom": { 2634 + "version": "19.2.3", 2635 + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", 2636 + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", 2637 + "dev": true, 2638 + "license": "MIT", 2639 + "peerDependencies": { 2640 + "@types/react": "^19.2.0" 2641 + } 2642 + }, 1802 2643 "node_modules/@types/send": { 1803 2644 "version": "1.2.1", 1804 2645 "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", ··· 1850 2691 "@types/node": "*" 1851 2692 } 1852 2693 }, 2694 + "node_modules/@vitejs/plugin-react": { 2695 + "version": "5.1.4", 2696 + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", 2697 + "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", 2698 + "dev": true, 2699 + "license": "MIT", 2700 + "dependencies": { 2701 + "@babel/core": "^7.29.0", 2702 + "@babel/plugin-transform-react-jsx-self": "^7.27.1", 2703 + "@babel/plugin-transform-react-jsx-source": "^7.27.1", 2704 + "@rolldown/pluginutils": "1.0.0-rc.3", 2705 + "@types/babel__core": "^7.20.5", 2706 + "react-refresh": "^0.18.0" 2707 + }, 2708 + "engines": { 2709 + "node": "^20.19.0 || >=22.12.0" 2710 + }, 2711 + "peerDependencies": { 2712 + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" 2713 + } 2714 + }, 1853 2715 "node_modules/accepts": { 1854 2716 "version": "2.0.0", 1855 2717 "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", ··· 1896 2758 "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1897 2759 } 1898 2760 }, 2761 + "node_modules/any-promise": { 2762 + "version": "1.3.0", 2763 + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 2764 + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", 2765 + "dev": true, 2766 + "license": "MIT" 2767 + }, 2768 + "node_modules/anymatch": { 2769 + "version": "3.1.3", 2770 + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 2771 + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 2772 + "dev": true, 2773 + "license": "ISC", 2774 + "dependencies": { 2775 + "normalize-path": "^3.0.0", 2776 + "picomatch": "^2.0.4" 2777 + }, 2778 + "engines": { 2779 + "node": ">= 8" 2780 + } 2781 + }, 2782 + "node_modules/arg": { 2783 + "version": "5.0.2", 2784 + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 2785 + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", 2786 + "dev": true, 2787 + "license": "MIT" 2788 + }, 1899 2789 "node_modules/ast-types": { 1900 2790 "version": "0.13.4", 1901 2791 "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", ··· 1914 2804 "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 1915 2805 "license": "MIT" 1916 2806 }, 2807 + "node_modules/autoprefixer": { 2808 + "version": "10.4.24", 2809 + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", 2810 + "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", 2811 + "dev": true, 2812 + "funding": [ 2813 + { 2814 + "type": "opencollective", 2815 + "url": "https://opencollective.com/postcss/" 2816 + }, 2817 + { 2818 + "type": "tidelift", 2819 + "url": "https://tidelift.com/funding/github/npm/autoprefixer" 2820 + }, 2821 + { 2822 + "type": "github", 2823 + "url": "https://github.com/sponsors/ai" 2824 + } 2825 + ], 2826 + "license": "MIT", 2827 + "dependencies": { 2828 + "browserslist": "^4.28.1", 2829 + "caniuse-lite": "^1.0.30001766", 2830 + "fraction.js": "^5.3.4", 2831 + "picocolors": "^1.1.1", 2832 + "postcss-value-parser": "^4.2.0" 2833 + }, 2834 + "bin": { 2835 + "autoprefixer": "bin/autoprefixer" 2836 + }, 2837 + "engines": { 2838 + "node": "^10 || ^12 || >=14" 2839 + }, 2840 + "peerDependencies": { 2841 + "postcss": "^8.1.0" 2842 + } 2843 + }, 1917 2844 "node_modules/await-lock": { 1918 2845 "version": "2.2.2", 1919 2846 "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", ··· 1921 2848 "license": "MIT" 1922 2849 }, 1923 2850 "node_modules/axios": { 1924 - "version": "1.13.2", 1925 - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", 1926 - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", 2851 + "version": "1.13.5", 2852 + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", 2853 + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", 1927 2854 "license": "MIT", 1928 2855 "dependencies": { 1929 - "follow-redirects": "^1.15.6", 1930 - "form-data": "^4.0.4", 2856 + "follow-redirects": "^1.15.11", 2857 + "form-data": "^4.0.5", 1931 2858 "proxy-from-env": "^1.1.0" 1932 2859 } 1933 2860 }, 1934 2861 "node_modules/b4a": { 1935 - "version": "1.7.3", 1936 - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", 1937 - "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", 2862 + "version": "1.7.4", 2863 + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.4.tgz", 2864 + "integrity": "sha512-u20zJLDaSWpxaZ+zaAkEIB2dZZ1o+DF4T/MRbmsvGp9nletHOyiai19OzX1fF8xUBYsO1bPXxODvcd0978pnug==", 1938 2865 "license": "Apache-2.0", 1939 2866 "peerDependencies": { 1940 2867 "react-native-b4a": "*" ··· 1960 2887 } 1961 2888 }, 1962 2889 "node_modules/bare-fs": { 1963 - "version": "4.5.2", 1964 - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.2.tgz", 1965 - "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==", 2890 + "version": "4.5.4", 2891 + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.4.tgz", 2892 + "integrity": "sha512-POK4oplfA7P7gqvetNmCs4CNtm9fNsx+IAh7jH7GgU0OJdge2rso0R20TNWVq6VoWcCvsTdlNDaleLHGaKx8CA==", 1966 2893 "license": "Apache-2.0", 1967 2894 "optional": true, 1968 2895 "dependencies": { ··· 2056 2983 ], 2057 2984 "license": "MIT" 2058 2985 }, 2986 + "node_modules/baseline-browser-mapping": { 2987 + "version": "2.9.19", 2988 + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", 2989 + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", 2990 + "dev": true, 2991 + "license": "Apache-2.0", 2992 + "bin": { 2993 + "baseline-browser-mapping": "dist/cli.js" 2994 + } 2995 + }, 2059 2996 "node_modules/basic-ftp": { 2060 2997 "version": "5.1.0", 2061 2998 "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", ··· 2075 3012 } 2076 3013 }, 2077 3014 "node_modules/better-sqlite3": { 2078 - "version": "12.5.0", 2079 - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.5.0.tgz", 2080 - "integrity": "sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==", 3015 + "version": "12.6.2", 3016 + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", 3017 + "integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==", 2081 3018 "hasInstallScript": true, 2082 3019 "license": "MIT", 2083 3020 "dependencies": { ··· 2088 3025 "node": "20.x || 22.x || 23.x || 24.x || 25.x" 2089 3026 } 2090 3027 }, 3028 + "node_modules/binary-extensions": { 3029 + "version": "2.3.0", 3030 + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 3031 + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 3032 + "dev": true, 3033 + "license": "MIT", 3034 + "engines": { 3035 + "node": ">=8" 3036 + }, 3037 + "funding": { 3038 + "url": "https://github.com/sponsors/sindresorhus" 3039 + } 3040 + }, 2091 3041 "node_modules/bindings": { 2092 3042 "version": "1.5.0", 2093 3043 "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", ··· 2133 3083 } 2134 3084 }, 2135 3085 "node_modules/body-parser/node_modules/iconv-lite": { 2136 - "version": "0.7.1", 2137 - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", 2138 - "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", 3086 + "version": "0.7.2", 3087 + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", 3088 + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", 2139 3089 "license": "MIT", 2140 3090 "dependencies": { 2141 3091 "safer-buffer": ">= 2.1.2 < 3.0.0" ··· 2154 3104 "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", 2155 3105 "license": "ISC" 2156 3106 }, 3107 + "node_modules/braces": { 3108 + "version": "3.0.3", 3109 + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 3110 + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 3111 + "dev": true, 3112 + "license": "MIT", 3113 + "dependencies": { 3114 + "fill-range": "^7.1.1" 3115 + }, 3116 + "engines": { 3117 + "node": ">=8" 3118 + } 3119 + }, 3120 + "node_modules/browserslist": { 3121 + "version": "4.28.1", 3122 + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", 3123 + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", 3124 + "dev": true, 3125 + "funding": [ 3126 + { 3127 + "type": "opencollective", 3128 + "url": "https://opencollective.com/browserslist" 3129 + }, 3130 + { 3131 + "type": "tidelift", 3132 + "url": "https://tidelift.com/funding/github/npm/browserslist" 3133 + }, 3134 + { 3135 + "type": "github", 3136 + "url": "https://github.com/sponsors/ai" 3137 + } 3138 + ], 3139 + "license": "MIT", 3140 + "dependencies": { 3141 + "baseline-browser-mapping": "^2.9.0", 3142 + "caniuse-lite": "^1.0.30001759", 3143 + "electron-to-chromium": "^1.5.263", 3144 + "node-releases": "^2.0.27", 3145 + "update-browserslist-db": "^1.2.0" 3146 + }, 3147 + "bin": { 3148 + "browserslist": "cli.js" 3149 + }, 3150 + "engines": { 3151 + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 3152 + } 3153 + }, 2157 3154 "node_modules/buffer": { 2158 3155 "version": "5.7.1", 2159 3156 "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", ··· 2249 3246 "url": "https://github.com/sponsors/ljharb" 2250 3247 } 2251 3248 }, 3249 + "node_modules/camelcase-css": { 3250 + "version": "2.0.1", 3251 + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", 3252 + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", 3253 + "dev": true, 3254 + "license": "MIT", 3255 + "engines": { 3256 + "node": ">= 6" 3257 + } 3258 + }, 3259 + "node_modules/caniuse-lite": { 3260 + "version": "1.0.30001769", 3261 + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", 3262 + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", 3263 + "dev": true, 3264 + "funding": [ 3265 + { 3266 + "type": "opencollective", 3267 + "url": "https://opencollective.com/browserslist" 3268 + }, 3269 + { 3270 + "type": "tidelift", 3271 + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 3272 + }, 3273 + { 3274 + "type": "github", 3275 + "url": "https://github.com/sponsors/ai" 3276 + } 3277 + ], 3278 + "license": "CC-BY-4.0" 3279 + }, 2252 3280 "node_modules/chardet": { 2253 3281 "version": "2.1.1", 2254 3282 "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", ··· 2256 3284 "license": "MIT" 2257 3285 }, 2258 3286 "node_modules/cheerio": { 2259 - "version": "1.1.2", 2260 - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", 2261 - "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", 3287 + "version": "1.2.0", 3288 + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", 3289 + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", 2262 3290 "license": "MIT", 2263 3291 "dependencies": { 2264 3292 "cheerio-select": "^2.1.0", ··· 2266 3294 "domhandler": "^5.0.3", 2267 3295 "domutils": "^3.2.2", 2268 3296 "encoding-sniffer": "^0.2.1", 2269 - "htmlparser2": "^10.0.0", 3297 + "htmlparser2": "^10.1.0", 2270 3298 "parse5": "^7.3.0", 2271 3299 "parse5-htmlparser2-tree-adapter": "^7.1.0", 2272 3300 "parse5-parser-stream": "^7.1.2", 2273 - "undici": "^7.12.0", 3301 + "undici": "^7.19.0", 2274 3302 "whatwg-mimetype": "^4.0.0" 2275 3303 }, 2276 3304 "engines": { ··· 2297 3325 "url": "https://github.com/sponsors/fb55" 2298 3326 } 2299 3327 }, 3328 + "node_modules/chokidar": { 3329 + "version": "3.6.0", 3330 + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 3331 + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 3332 + "dev": true, 3333 + "license": "MIT", 3334 + "dependencies": { 3335 + "anymatch": "~3.1.2", 3336 + "braces": "~3.0.2", 3337 + "glob-parent": "~5.1.2", 3338 + "is-binary-path": "~2.1.0", 3339 + "is-glob": "~4.0.1", 3340 + "normalize-path": "~3.0.0", 3341 + "readdirp": "~3.6.0" 3342 + }, 3343 + "engines": { 3344 + "node": ">= 8.10.0" 3345 + }, 3346 + "funding": { 3347 + "url": "https://paulmillr.com/funding/" 3348 + }, 3349 + "optionalDependencies": { 3350 + "fsevents": "~2.3.2" 3351 + } 3352 + }, 3353 + "node_modules/chokidar/node_modules/glob-parent": { 3354 + "version": "5.1.2", 3355 + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 3356 + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 3357 + "dev": true, 3358 + "license": "ISC", 3359 + "dependencies": { 3360 + "is-glob": "^4.0.1" 3361 + }, 3362 + "engines": { 3363 + "node": ">= 6" 3364 + } 3365 + }, 2300 3366 "node_modules/chownr": { 2301 3367 "version": "1.1.4", 2302 3368 "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", ··· 2304 3370 "license": "ISC" 2305 3371 }, 2306 3372 "node_modules/chromium-bidi": { 2307 - "version": "12.0.1", 2308 - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-12.0.1.tgz", 2309 - "integrity": "sha512-fGg+6jr0xjQhzpy5N4ErZxQ4wF7KLEvhGZXD6EgvZKDhu7iOhZXnZhcDxPJDcwTcrD48NPzOCo84RP2lv3Z+Cg==", 3373 + "version": "14.0.0", 3374 + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", 3375 + "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==", 2310 3376 "license": "Apache-2.0", 2311 3377 "dependencies": { 2312 3378 "mitt": "^3.0.1", ··· 2314 3380 }, 2315 3381 "peerDependencies": { 2316 3382 "devtools-protocol": "*" 3383 + } 3384 + }, 3385 + "node_modules/class-variance-authority": { 3386 + "version": "0.7.1", 3387 + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", 3388 + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", 3389 + "license": "Apache-2.0", 3390 + "dependencies": { 3391 + "clsx": "^2.1.1" 3392 + }, 3393 + "funding": { 3394 + "url": "https://polar.sh/cva" 2317 3395 } 2318 3396 }, 2319 3397 "node_modules/cli-width": { ··· 2412 3490 "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 2413 3491 } 2414 3492 }, 3493 + "node_modules/clsx": { 3494 + "version": "2.1.1", 3495 + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", 3496 + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", 3497 + "license": "MIT", 3498 + "engines": { 3499 + "node": ">=6" 3500 + } 3501 + }, 2415 3502 "node_modules/collapse-white-space": { 2416 3503 "version": "2.1.0", 2417 3504 "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", ··· 2453 3540 } 2454 3541 }, 2455 3542 "node_modules/commander": { 2456 - "version": "14.0.2", 2457 - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", 2458 - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", 3543 + "version": "14.0.3", 3544 + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", 3545 + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", 2459 3546 "license": "MIT", 2460 3547 "engines": { 2461 3548 "node": ">=20" ··· 2483 3570 "node": ">= 0.6" 2484 3571 } 2485 3572 }, 3573 + "node_modules/convert-source-map": { 3574 + "version": "2.0.0", 3575 + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", 3576 + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", 3577 + "dev": true, 3578 + "license": "MIT" 3579 + }, 2486 3580 "node_modules/cookie": { 2487 3581 "version": "0.7.2", 2488 3582 "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", ··· 2502 3596 } 2503 3597 }, 2504 3598 "node_modules/cors": { 2505 - "version": "2.8.5", 2506 - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 2507 - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 3599 + "version": "2.8.6", 3600 + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", 3601 + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", 2508 3602 "license": "MIT", 2509 3603 "dependencies": { 2510 3604 "object-assign": "^4", ··· 2512 3606 }, 2513 3607 "engines": { 2514 3608 "node": ">= 0.10" 3609 + }, 3610 + "funding": { 3611 + "type": "opencollective", 3612 + "url": "https://opencollective.com/express" 2515 3613 } 2516 3614 }, 2517 3615 "node_modules/cross-fetch": { ··· 2551 3649 "url": "https://github.com/sponsors/fb55" 2552 3650 } 2553 3651 }, 3652 + "node_modules/cssesc": { 3653 + "version": "3.0.0", 3654 + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 3655 + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 3656 + "dev": true, 3657 + "license": "MIT", 3658 + "bin": { 3659 + "cssesc": "bin/cssesc" 3660 + }, 3661 + "engines": { 3662 + "node": ">=4" 3663 + } 3664 + }, 2554 3665 "node_modules/cssom": { 2555 3666 "version": "0.5.0", 2556 3667 "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", 2557 3668 "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", 3669 + "license": "MIT" 3670 + }, 3671 + "node_modules/csstype": { 3672 + "version": "3.2.3", 3673 + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", 3674 + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", 3675 + "dev": true, 2558 3676 "license": "MIT" 2559 3677 }, 2560 3678 "node_modules/data-uri-to-buffer": { ··· 2666 3784 } 2667 3785 }, 2668 3786 "node_modules/devtools-protocol": { 2669 - "version": "0.0.1534754", 2670 - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1534754.tgz", 2671 - "integrity": "sha512-26T91cV5dbOYnXdJi5qQHoTtUoNEqwkHcAyu/IKtjIAxiEqPMrDiRkDOPWVsGfNZGmlQVHQbZRSjD8sxagWVsQ==", 3787 + "version": "0.0.1566079", 3788 + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz", 3789 + "integrity": "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==", 2672 3790 "license": "BSD-3-Clause" 3791 + }, 3792 + "node_modules/didyoumean": { 3793 + "version": "1.2.2", 3794 + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", 3795 + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", 3796 + "dev": true, 3797 + "license": "Apache-2.0" 3798 + }, 3799 + "node_modules/dlv": { 3800 + "version": "1.1.3", 3801 + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", 3802 + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", 3803 + "dev": true, 3804 + "license": "MIT" 2673 3805 }, 2674 3806 "node_modules/dom-serializer": { 2675 3807 "version": "2.0.0", ··· 2727 3859 } 2728 3860 }, 2729 3861 "node_modules/dotenv": { 2730 - "version": "17.2.3", 2731 - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", 2732 - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", 3862 + "version": "17.3.1", 3863 + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", 3864 + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", 2733 3865 "license": "BSD-2-Clause", 2734 3866 "engines": { 2735 3867 "node": ">=12" ··· 2766 3898 "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 2767 3899 "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", 2768 3900 "license": "MIT" 3901 + }, 3902 + "node_modules/electron-to-chromium": { 3903 + "version": "1.5.286", 3904 + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", 3905 + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", 3906 + "dev": true, 3907 + "license": "ISC" 2769 3908 }, 2770 3909 "node_modules/emoji-regex": { 2771 3910 "version": "10.6.0", ··· 2862 4001 } 2863 4002 }, 2864 4003 "node_modules/esbuild": { 2865 - "version": "0.27.2", 2866 - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", 2867 - "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", 4004 + "version": "0.27.3", 4005 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", 4006 + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", 2868 4007 "dev": true, 2869 4008 "hasInstallScript": true, 2870 4009 "license": "MIT", ··· 2875 4014 "node": ">=18" 2876 4015 }, 2877 4016 "optionalDependencies": { 2878 - "@esbuild/aix-ppc64": "0.27.2", 2879 - "@esbuild/android-arm": "0.27.2", 2880 - "@esbuild/android-arm64": "0.27.2", 2881 - "@esbuild/android-x64": "0.27.2", 2882 - "@esbuild/darwin-arm64": "0.27.2", 2883 - "@esbuild/darwin-x64": "0.27.2", 2884 - "@esbuild/freebsd-arm64": "0.27.2", 2885 - "@esbuild/freebsd-x64": "0.27.2", 2886 - "@esbuild/linux-arm": "0.27.2", 2887 - "@esbuild/linux-arm64": "0.27.2", 2888 - "@esbuild/linux-ia32": "0.27.2", 2889 - "@esbuild/linux-loong64": "0.27.2", 2890 - "@esbuild/linux-mips64el": "0.27.2", 2891 - "@esbuild/linux-ppc64": "0.27.2", 2892 - "@esbuild/linux-riscv64": "0.27.2", 2893 - "@esbuild/linux-s390x": "0.27.2", 2894 - "@esbuild/linux-x64": "0.27.2", 2895 - "@esbuild/netbsd-arm64": "0.27.2", 2896 - "@esbuild/netbsd-x64": "0.27.2", 2897 - "@esbuild/openbsd-arm64": "0.27.2", 2898 - "@esbuild/openbsd-x64": "0.27.2", 2899 - "@esbuild/openharmony-arm64": "0.27.2", 2900 - "@esbuild/sunos-x64": "0.27.2", 2901 - "@esbuild/win32-arm64": "0.27.2", 2902 - "@esbuild/win32-ia32": "0.27.2", 2903 - "@esbuild/win32-x64": "0.27.2" 4017 + "@esbuild/aix-ppc64": "0.27.3", 4018 + "@esbuild/android-arm": "0.27.3", 4019 + "@esbuild/android-arm64": "0.27.3", 4020 + "@esbuild/android-x64": "0.27.3", 4021 + "@esbuild/darwin-arm64": "0.27.3", 4022 + "@esbuild/darwin-x64": "0.27.3", 4023 + "@esbuild/freebsd-arm64": "0.27.3", 4024 + "@esbuild/freebsd-x64": "0.27.3", 4025 + "@esbuild/linux-arm": "0.27.3", 4026 + "@esbuild/linux-arm64": "0.27.3", 4027 + "@esbuild/linux-ia32": "0.27.3", 4028 + "@esbuild/linux-loong64": "0.27.3", 4029 + "@esbuild/linux-mips64el": "0.27.3", 4030 + "@esbuild/linux-ppc64": "0.27.3", 4031 + "@esbuild/linux-riscv64": "0.27.3", 4032 + "@esbuild/linux-s390x": "0.27.3", 4033 + "@esbuild/linux-x64": "0.27.3", 4034 + "@esbuild/netbsd-arm64": "0.27.3", 4035 + "@esbuild/netbsd-x64": "0.27.3", 4036 + "@esbuild/openbsd-arm64": "0.27.3", 4037 + "@esbuild/openbsd-x64": "0.27.3", 4038 + "@esbuild/openharmony-arm64": "0.27.3", 4039 + "@esbuild/sunos-x64": "0.27.3", 4040 + "@esbuild/win32-arm64": "0.27.3", 4041 + "@esbuild/win32-ia32": "0.27.3", 4042 + "@esbuild/win32-x64": "0.27.3" 2904 4043 } 2905 4044 }, 2906 4045 "node_modules/escalade": { ··· 3066 4205 "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", 3067 4206 "license": "MIT" 3068 4207 }, 4208 + "node_modules/fast-glob": { 4209 + "version": "3.3.3", 4210 + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", 4211 + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", 4212 + "dev": true, 4213 + "license": "MIT", 4214 + "dependencies": { 4215 + "@nodelib/fs.stat": "^2.0.2", 4216 + "@nodelib/fs.walk": "^1.2.3", 4217 + "glob-parent": "^5.1.2", 4218 + "merge2": "^1.3.0", 4219 + "micromatch": "^4.0.8" 4220 + }, 4221 + "engines": { 4222 + "node": ">=8.6.0" 4223 + } 4224 + }, 4225 + "node_modules/fast-glob/node_modules/glob-parent": { 4226 + "version": "5.1.2", 4227 + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 4228 + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 4229 + "dev": true, 4230 + "license": "ISC", 4231 + "dependencies": { 4232 + "is-glob": "^4.0.1" 4233 + }, 4234 + "engines": { 4235 + "node": ">= 6" 4236 + } 4237 + }, 4238 + "node_modules/fastq": { 4239 + "version": "1.20.1", 4240 + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", 4241 + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", 4242 + "dev": true, 4243 + "license": "ISC", 4244 + "dependencies": { 4245 + "reusify": "^1.0.4" 4246 + } 4247 + }, 3069 4248 "node_modules/fd-slicer": { 3070 4249 "version": "1.1.0", 3071 4250 "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", ··· 3081 4260 "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", 3082 4261 "license": "MIT" 3083 4262 }, 4263 + "node_modules/fill-range": { 4264 + "version": "7.1.1", 4265 + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 4266 + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 4267 + "dev": true, 4268 + "license": "MIT", 4269 + "dependencies": { 4270 + "to-regex-range": "^5.0.1" 4271 + }, 4272 + "engines": { 4273 + "node": ">=8" 4274 + } 4275 + }, 3084 4276 "node_modules/finalhandler": { 3085 4277 "version": "2.1.1", 3086 4278 "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", ··· 3168 4360 "node": ">= 0.6" 3169 4361 } 3170 4362 }, 4363 + "node_modules/fraction.js": { 4364 + "version": "5.3.4", 4365 + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", 4366 + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", 4367 + "dev": true, 4368 + "license": "MIT", 4369 + "engines": { 4370 + "node": "*" 4371 + }, 4372 + "funding": { 4373 + "type": "github", 4374 + "url": "https://github.com/sponsors/rawify" 4375 + } 4376 + }, 3171 4377 "node_modules/franc-min": { 3172 4378 "version": "6.2.0", 3173 4379 "resolved": "https://registry.npmjs.org/franc-min/-/franc-min-6.2.0.tgz", ··· 3220 4426 "url": "https://github.com/sponsors/ljharb" 3221 4427 } 3222 4428 }, 4429 + "node_modules/gensync": { 4430 + "version": "1.0.0-beta.2", 4431 + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 4432 + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 4433 + "dev": true, 4434 + "license": "MIT", 4435 + "engines": { 4436 + "node": ">=6.9.0" 4437 + } 4438 + }, 3223 4439 "node_modules/get-caller-file": { 3224 4440 "version": "2.0.5", 3225 4441 "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", ··· 3294 4510 } 3295 4511 }, 3296 4512 "node_modules/get-tsconfig": { 3297 - "version": "4.13.0", 3298 - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", 3299 - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", 4513 + "version": "4.13.6", 4514 + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", 4515 + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", 3300 4516 "dev": true, 3301 4517 "license": "MIT", 3302 4518 "dependencies": { ··· 3326 4542 "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", 3327 4543 "license": "MIT" 3328 4544 }, 4545 + "node_modules/glob-parent": { 4546 + "version": "6.0.2", 4547 + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 4548 + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 4549 + "dev": true, 4550 + "license": "ISC", 4551 + "dependencies": { 4552 + "is-glob": "^4.0.3" 4553 + }, 4554 + "engines": { 4555 + "node": ">=10.13.0" 4556 + } 4557 + }, 3329 4558 "node_modules/gopd": { 3330 4559 "version": "1.2.0", 3331 4560 "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", ··· 3402 4631 "license": "MIT" 3403 4632 }, 3404 4633 "node_modules/htmlparser2": { 3405 - "version": "10.0.0", 3406 - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", 3407 - "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", 4634 + "version": "10.1.0", 4635 + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", 4636 + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", 3408 4637 "funding": [ 3409 4638 "https://github.com/fb55/htmlparser2?sponsor=1", 3410 4639 { ··· 3416 4645 "dependencies": { 3417 4646 "domelementtype": "^2.3.0", 3418 4647 "domhandler": "^5.0.3", 3419 - "domutils": "^3.2.1", 3420 - "entities": "^6.0.0" 4648 + "domutils": "^3.2.2", 4649 + "entities": "^7.0.1" 3421 4650 } 3422 4651 }, 3423 4652 "node_modules/htmlparser2/node_modules/entities": { 3424 - "version": "6.0.1", 3425 - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", 3426 - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", 4653 + "version": "7.0.1", 4654 + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", 4655 + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", 3427 4656 "license": "BSD-2-Clause", 3428 4657 "engines": { 3429 4658 "node": ">=0.12" ··· 3523 4752 "license": "ISC" 3524 4753 }, 3525 4754 "node_modules/inquirer": { 3526 - "version": "13.1.0", 3527 - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-13.1.0.tgz", 3528 - "integrity": "sha512-4vv4GS/9HLnn0radvmHlXUXiNkd2gYCBQ4U1rxZWBJDisu2Z06bzUM9CFU8pcu1vwuAQjo6O+CFiqCYNsEi6qQ==", 4755 + "version": "13.2.2", 4756 + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-13.2.2.tgz", 4757 + "integrity": "sha512-+hlN8I88JE9T3zjWHGnMhryniRDbSgFNJHJTyD2iKO5YNpMRyfghQ6wVoe+gV4ygMM4r4GzlsBxNa1g/UUZixA==", 3529 4758 "license": "MIT", 3530 4759 "dependencies": { 3531 - "@inquirer/ansi": "^2.0.2", 3532 - "@inquirer/core": "^11.1.0", 3533 - "@inquirer/prompts": "^8.1.0", 3534 - "@inquirer/type": "^4.0.2", 4760 + "@inquirer/ansi": "^2.0.3", 4761 + "@inquirer/core": "^11.1.1", 4762 + "@inquirer/prompts": "^8.2.0", 4763 + "@inquirer/type": "^4.0.3", 3535 4764 "mute-stream": "^3.0.0", 3536 4765 "run-async": "^4.0.6", 3537 4766 "rxjs": "^7.8.2" ··· 3566 4795 "node": ">= 0.10" 3567 4796 } 3568 4797 }, 4798 + "node_modules/is-binary-path": { 4799 + "version": "2.1.0", 4800 + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 4801 + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 4802 + "dev": true, 4803 + "license": "MIT", 4804 + "dependencies": { 4805 + "binary-extensions": "^2.0.0" 4806 + }, 4807 + "engines": { 4808 + "node": ">=8" 4809 + } 4810 + }, 4811 + "node_modules/is-core-module": { 4812 + "version": "2.16.1", 4813 + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", 4814 + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", 4815 + "dev": true, 4816 + "license": "MIT", 4817 + "dependencies": { 4818 + "hasown": "^2.0.2" 4819 + }, 4820 + "engines": { 4821 + "node": ">= 0.4" 4822 + }, 4823 + "funding": { 4824 + "url": "https://github.com/sponsors/ljharb" 4825 + } 4826 + }, 4827 + "node_modules/is-extglob": { 4828 + "version": "2.1.1", 4829 + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 4830 + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 4831 + "dev": true, 4832 + "license": "MIT", 4833 + "engines": { 4834 + "node": ">=0.10.0" 4835 + } 4836 + }, 3569 4837 "node_modules/is-fullwidth-code-point": { 3570 4838 "version": "3.0.0", 3571 4839 "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", ··· 3575 4843 "node": ">=8" 3576 4844 } 3577 4845 }, 4846 + "node_modules/is-glob": { 4847 + "version": "4.0.3", 4848 + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 4849 + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 4850 + "dev": true, 4851 + "license": "MIT", 4852 + "dependencies": { 4853 + "is-extglob": "^2.1.1" 4854 + }, 4855 + "engines": { 4856 + "node": ">=0.10.0" 4857 + } 4858 + }, 4859 + "node_modules/is-number": { 4860 + "version": "7.0.0", 4861 + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 4862 + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 4863 + "dev": true, 4864 + "license": "MIT", 4865 + "engines": { 4866 + "node": ">=0.12.0" 4867 + } 4868 + }, 3578 4869 "node_modules/is-promise": { 3579 4870 "version": "4.0.0", 3580 4871 "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", ··· 3602 4893 "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 3603 4894 "license": "MIT" 3604 4895 }, 4896 + "node_modules/jiti": { 4897 + "version": "1.21.7", 4898 + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", 4899 + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", 4900 + "dev": true, 4901 + "license": "MIT", 4902 + "bin": { 4903 + "jiti": "bin/jiti.js" 4904 + } 4905 + }, 4906 + "node_modules/js-tokens": { 4907 + "version": "4.0.0", 4908 + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 4909 + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 4910 + "dev": true, 4911 + "license": "MIT" 4912 + }, 4913 + "node_modules/jsesc": { 4914 + "version": "3.1.0", 4915 + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", 4916 + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", 4917 + "dev": true, 4918 + "license": "MIT", 4919 + "bin": { 4920 + "jsesc": "bin/jsesc" 4921 + }, 4922 + "engines": { 4923 + "node": ">=6" 4924 + } 4925 + }, 3605 4926 "node_modules/json-stable-stringify": { 3606 4927 "version": "1.3.0", 3607 4928 "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz", ··· 3621 4942 "url": "https://github.com/sponsors/ljharb" 3622 4943 } 3623 4944 }, 4945 + "node_modules/json5": { 4946 + "version": "2.2.3", 4947 + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", 4948 + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", 4949 + "dev": true, 4950 + "license": "MIT", 4951 + "bin": { 4952 + "json5": "lib/cli.js" 4953 + }, 4954 + "engines": { 4955 + "node": ">=6" 4956 + } 4957 + }, 3624 4958 "node_modules/jsonify": { 3625 4959 "version": "0.0.1", 3626 4960 "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", ··· 3652 4986 "npm": ">=6" 3653 4987 } 3654 4988 }, 4989 + "node_modules/jsonwebtoken/node_modules/semver": { 4990 + "version": "7.7.4", 4991 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", 4992 + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", 4993 + "license": "ISC", 4994 + "bin": { 4995 + "semver": "bin/semver.js" 4996 + }, 4997 + "engines": { 4998 + "node": ">=10" 4999 + } 5000 + }, 3655 5001 "node_modules/jwa": { 3656 5002 "version": "2.0.1", 3657 5003 "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", ··· 3673 5019 "safe-buffer": "^5.0.1" 3674 5020 } 3675 5021 }, 5022 + "node_modules/lilconfig": { 5023 + "version": "3.1.3", 5024 + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", 5025 + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", 5026 + "dev": true, 5027 + "license": "MIT", 5028 + "engines": { 5029 + "node": ">=14" 5030 + }, 5031 + "funding": { 5032 + "url": "https://github.com/sponsors/antonk52" 5033 + } 5034 + }, 5035 + "node_modules/lines-and-columns": { 5036 + "version": "1.2.4", 5037 + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", 5038 + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", 5039 + "dev": true, 5040 + "license": "MIT" 5041 + }, 3676 5042 "node_modules/linkedom": { 3677 5043 "version": "0.18.12", 3678 5044 "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.12.tgz", ··· 3740 5106 "license": "MIT" 3741 5107 }, 3742 5108 "node_modules/lru-cache": { 3743 - "version": "7.18.3", 3744 - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", 3745 - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", 5109 + "version": "5.1.1", 5110 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 5111 + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 5112 + "dev": true, 5113 + "license": "ISC", 5114 + "dependencies": { 5115 + "yallist": "^3.0.2" 5116 + } 5117 + }, 5118 + "node_modules/lucide-react": { 5119 + "version": "0.553.0", 5120 + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.553.0.tgz", 5121 + "integrity": "sha512-BRgX5zrWmNy/lkVAe0dXBgd7XQdZ3HTf+Hwe3c9WK6dqgnj9h+hxV+MDncM88xDWlCq27+TKvHGE70ViODNILw==", 3746 5122 "license": "ISC", 3747 - "engines": { 3748 - "node": ">=12" 5123 + "peerDependencies": { 5124 + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" 3749 5125 } 3750 5126 }, 3751 5127 "node_modules/math-intrinsics": { ··· 3778 5154 "url": "https://github.com/sponsors/sindresorhus" 3779 5155 } 3780 5156 }, 5157 + "node_modules/merge2": { 5158 + "version": "1.4.1", 5159 + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 5160 + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 5161 + "dev": true, 5162 + "license": "MIT", 5163 + "engines": { 5164 + "node": ">= 8" 5165 + } 5166 + }, 5167 + "node_modules/micromatch": { 5168 + "version": "4.0.8", 5169 + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", 5170 + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", 5171 + "dev": true, 5172 + "license": "MIT", 5173 + "dependencies": { 5174 + "braces": "^3.0.3", 5175 + "picomatch": "^2.3.1" 5176 + }, 5177 + "engines": { 5178 + "node": ">=8.6" 5179 + } 5180 + }, 3781 5181 "node_modules/mime-db": { 3782 5182 "version": "1.54.0", 3783 5183 "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", ··· 3857 5257 "node": "^20.17.0 || >=22.9.0" 3858 5258 } 3859 5259 }, 5260 + "node_modules/mz": { 5261 + "version": "2.7.0", 5262 + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", 5263 + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", 5264 + "dev": true, 5265 + "license": "MIT", 5266 + "dependencies": { 5267 + "any-promise": "^1.0.0", 5268 + "object-assign": "^4.0.1", 5269 + "thenify-all": "^1.0.0" 5270 + } 5271 + }, 3860 5272 "node_modules/n-gram": { 3861 5273 "version": "2.0.2", 3862 5274 "resolved": "https://registry.npmjs.org/n-gram/-/n-gram-2.0.2.tgz", ··· 3867 5279 "url": "https://github.com/sponsors/wooorm" 3868 5280 } 3869 5281 }, 5282 + "node_modules/nanoid": { 5283 + "version": "3.3.11", 5284 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 5285 + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 5286 + "dev": true, 5287 + "funding": [ 5288 + { 5289 + "type": "github", 5290 + "url": "https://github.com/sponsors/ai" 5291 + } 5292 + ], 5293 + "license": "MIT", 5294 + "bin": { 5295 + "nanoid": "bin/nanoid.cjs" 5296 + }, 5297 + "engines": { 5298 + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 5299 + } 5300 + }, 3870 5301 "node_modules/napi-build-utils": { 3871 5302 "version": "2.0.0", 3872 5303 "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", ··· 3892 5323 } 3893 5324 }, 3894 5325 "node_modules/node-abi": { 3895 - "version": "3.85.0", 3896 - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz", 3897 - "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==", 5326 + "version": "3.87.0", 5327 + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", 5328 + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", 3898 5329 "license": "MIT", 3899 5330 "dependencies": { 3900 5331 "semver": "^7.3.5" 5332 + }, 5333 + "engines": { 5334 + "node": ">=10" 5335 + } 5336 + }, 5337 + "node_modules/node-abi/node_modules/semver": { 5338 + "version": "7.7.4", 5339 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", 5340 + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", 5341 + "license": "ISC", 5342 + "bin": { 5343 + "semver": "bin/semver.js" 3901 5344 }, 3902 5345 "engines": { 3903 5346 "node": ">=10" ··· 3932 5375 } 3933 5376 } 3934 5377 }, 5378 + "node_modules/node-releases": { 5379 + "version": "2.0.27", 5380 + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", 5381 + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", 5382 + "dev": true, 5383 + "license": "MIT" 5384 + }, 5385 + "node_modules/normalize-path": { 5386 + "version": "3.0.0", 5387 + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 5388 + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 5389 + "dev": true, 5390 + "license": "MIT", 5391 + "engines": { 5392 + "node": ">=0.10.0" 5393 + } 5394 + }, 3935 5395 "node_modules/nth-check": { 3936 5396 "version": "2.1.1", 3937 5397 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", ··· 3951 5411 "license": "MIT", 3952 5412 "engines": { 3953 5413 "node": ">=0.10.0" 5414 + } 5415 + }, 5416 + "node_modules/object-hash": { 5417 + "version": "3.0.0", 5418 + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 5419 + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", 5420 + "dev": true, 5421 + "license": "MIT", 5422 + "engines": { 5423 + "node": ">= 6" 3954 5424 } 3955 5425 }, 3956 5426 "node_modules/object-inspect": { ··· 3996 5466 } 3997 5467 }, 3998 5468 "node_modules/otpauth": { 3999 - "version": "9.4.1", 4000 - "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.4.1.tgz", 4001 - "integrity": "sha512-+iVvys36CFsyXEqfNftQm1II7SW23W1wx9RwNk0Cd97lbvorqAhBDksb/0bYry087QMxjiuBS0wokdoZ0iUeAw==", 5469 + "version": "9.5.0", 5470 + "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.5.0.tgz", 5471 + "integrity": "sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==", 4002 5472 "license": "MIT", 4003 5473 "dependencies": { 4004 - "@noble/hashes": "1.8.0" 5474 + "@noble/hashes": "2.0.1" 4005 5475 }, 4006 5476 "funding": { 4007 5477 "url": "https://github.com/hectorm/otpauth?sponsor=1" ··· 4097 5567 "node": ">= 0.8" 4098 5568 } 4099 5569 }, 5570 + "node_modules/path-parse": { 5571 + "version": "1.0.7", 5572 + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 5573 + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 5574 + "dev": true, 5575 + "license": "MIT" 5576 + }, 4100 5577 "node_modules/path-to-regexp": { 4101 5578 "version": "8.3.0", 4102 5579 "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", ··· 4113 5590 "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", 4114 5591 "license": "MIT" 4115 5592 }, 5593 + "node_modules/picocolors": { 5594 + "version": "1.1.1", 5595 + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 5596 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 5597 + "dev": true, 5598 + "license": "ISC" 5599 + }, 5600 + "node_modules/picomatch": { 5601 + "version": "2.3.1", 5602 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 5603 + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 5604 + "dev": true, 5605 + "license": "MIT", 5606 + "engines": { 5607 + "node": ">=8.6" 5608 + }, 5609 + "funding": { 5610 + "url": "https://github.com/sponsors/jonschlinkert" 5611 + } 5612 + }, 5613 + "node_modules/pify": { 5614 + "version": "2.3.0", 5615 + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 5616 + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 5617 + "dev": true, 5618 + "license": "MIT", 5619 + "engines": { 5620 + "node": ">=0.10.0" 5621 + } 5622 + }, 5623 + "node_modules/pirates": { 5624 + "version": "4.0.7", 5625 + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", 5626 + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", 5627 + "dev": true, 5628 + "license": "MIT", 5629 + "engines": { 5630 + "node": ">= 6" 5631 + } 5632 + }, 5633 + "node_modules/postcss": { 5634 + "version": "8.5.6", 5635 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", 5636 + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", 5637 + "dev": true, 5638 + "funding": [ 5639 + { 5640 + "type": "opencollective", 5641 + "url": "https://opencollective.com/postcss/" 5642 + }, 5643 + { 5644 + "type": "tidelift", 5645 + "url": "https://tidelift.com/funding/github/npm/postcss" 5646 + }, 5647 + { 5648 + "type": "github", 5649 + "url": "https://github.com/sponsors/ai" 5650 + } 5651 + ], 5652 + "license": "MIT", 5653 + "dependencies": { 5654 + "nanoid": "^3.3.11", 5655 + "picocolors": "^1.1.1", 5656 + "source-map-js": "^1.2.1" 5657 + }, 5658 + "engines": { 5659 + "node": "^10 || ^12 || >=14" 5660 + } 5661 + }, 5662 + "node_modules/postcss-import": { 5663 + "version": "15.1.0", 5664 + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", 5665 + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", 5666 + "dev": true, 5667 + "license": "MIT", 5668 + "dependencies": { 5669 + "postcss-value-parser": "^4.0.0", 5670 + "read-cache": "^1.0.0", 5671 + "resolve": "^1.1.7" 5672 + }, 5673 + "engines": { 5674 + "node": ">=14.0.0" 5675 + }, 5676 + "peerDependencies": { 5677 + "postcss": "^8.0.0" 5678 + } 5679 + }, 5680 + "node_modules/postcss-js": { 5681 + "version": "4.1.0", 5682 + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", 5683 + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", 5684 + "dev": true, 5685 + "funding": [ 5686 + { 5687 + "type": "opencollective", 5688 + "url": "https://opencollective.com/postcss/" 5689 + }, 5690 + { 5691 + "type": "github", 5692 + "url": "https://github.com/sponsors/ai" 5693 + } 5694 + ], 5695 + "license": "MIT", 5696 + "dependencies": { 5697 + "camelcase-css": "^2.0.1" 5698 + }, 5699 + "engines": { 5700 + "node": "^12 || ^14 || >= 16" 5701 + }, 5702 + "peerDependencies": { 5703 + "postcss": "^8.4.21" 5704 + } 5705 + }, 5706 + "node_modules/postcss-load-config": { 5707 + "version": "6.0.1", 5708 + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", 5709 + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", 5710 + "dev": true, 5711 + "funding": [ 5712 + { 5713 + "type": "opencollective", 5714 + "url": "https://opencollective.com/postcss/" 5715 + }, 5716 + { 5717 + "type": "github", 5718 + "url": "https://github.com/sponsors/ai" 5719 + } 5720 + ], 5721 + "license": "MIT", 5722 + "dependencies": { 5723 + "lilconfig": "^3.1.1" 5724 + }, 5725 + "engines": { 5726 + "node": ">= 18" 5727 + }, 5728 + "peerDependencies": { 5729 + "jiti": ">=1.21.0", 5730 + "postcss": ">=8.0.9", 5731 + "tsx": "^4.8.1", 5732 + "yaml": "^2.4.2" 5733 + }, 5734 + "peerDependenciesMeta": { 5735 + "jiti": { 5736 + "optional": true 5737 + }, 5738 + "postcss": { 5739 + "optional": true 5740 + }, 5741 + "tsx": { 5742 + "optional": true 5743 + }, 5744 + "yaml": { 5745 + "optional": true 5746 + } 5747 + } 5748 + }, 5749 + "node_modules/postcss-nested": { 5750 + "version": "6.2.0", 5751 + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", 5752 + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", 5753 + "dev": true, 5754 + "funding": [ 5755 + { 5756 + "type": "opencollective", 5757 + "url": "https://opencollective.com/postcss/" 5758 + }, 5759 + { 5760 + "type": "github", 5761 + "url": "https://github.com/sponsors/ai" 5762 + } 5763 + ], 5764 + "license": "MIT", 5765 + "dependencies": { 5766 + "postcss-selector-parser": "^6.1.1" 5767 + }, 5768 + "engines": { 5769 + "node": ">=12.0" 5770 + }, 5771 + "peerDependencies": { 5772 + "postcss": "^8.2.14" 5773 + } 5774 + }, 5775 + "node_modules/postcss-selector-parser": { 5776 + "version": "6.1.2", 5777 + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", 5778 + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", 5779 + "dev": true, 5780 + "license": "MIT", 5781 + "dependencies": { 5782 + "cssesc": "^3.0.0", 5783 + "util-deprecate": "^1.0.2" 5784 + }, 5785 + "engines": { 5786 + "node": ">=4" 5787 + } 5788 + }, 5789 + "node_modules/postcss-value-parser": { 5790 + "version": "4.2.0", 5791 + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 5792 + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 5793 + "dev": true, 5794 + "license": "MIT" 5795 + }, 4116 5796 "node_modules/prebuild-install": { 4117 5797 "version": "7.1.3", 4118 5798 "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", ··· 4180 5860 "node": ">= 14" 4181 5861 } 4182 5862 }, 5863 + "node_modules/proxy-agent/node_modules/lru-cache": { 5864 + "version": "7.18.3", 5865 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", 5866 + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", 5867 + "license": "ISC", 5868 + "engines": { 5869 + "node": ">=12" 5870 + } 5871 + }, 4183 5872 "node_modules/proxy-from-env": { 4184 5873 "version": "1.1.0", 4185 5874 "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", ··· 4218 5907 } 4219 5908 }, 4220 5909 "node_modules/puppeteer-core": { 4221 - "version": "24.34.0", 4222 - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.34.0.tgz", 4223 - "integrity": "sha512-24evawO+mUGW4mvS2a2ivwLdX3gk8zRLZr9HP+7+VT2vBQnm0oh9jJEZmUE3ePJhRkYlZ93i7OMpdcoi2qNCLg==", 5910 + "version": "24.37.3", 5911 + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.37.3.tgz", 5912 + "integrity": "sha512-fokQ8gv+hNgsRWqVuP5rUjGp+wzV5aMTP3fcm8ekNabmLGlJdFHas1OdMscAH9Gzq4Qcf7cfI/Pe6wEcAqQhqg==", 4224 5913 "license": "Apache-2.0", 4225 5914 "dependencies": { 4226 - "@puppeteer/browsers": "2.11.0", 4227 - "chromium-bidi": "12.0.1", 5915 + "@puppeteer/browsers": "2.12.1", 5916 + "chromium-bidi": "14.0.0", 4228 5917 "debug": "^4.4.3", 4229 - "devtools-protocol": "0.0.1534754", 5918 + "devtools-protocol": "0.0.1566079", 4230 5919 "typed-query-selector": "^2.12.0", 4231 - "webdriver-bidi-protocol": "0.3.10", 4232 - "ws": "^8.18.3" 5920 + "webdriver-bidi-protocol": "0.4.1", 5921 + "ws": "^8.19.0" 4233 5922 }, 4234 5923 "engines": { 4235 5924 "node": ">=18" 4236 5925 } 4237 5926 }, 4238 5927 "node_modules/qs": { 4239 - "version": "6.14.1", 4240 - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", 4241 - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", 5928 + "version": "6.14.2", 5929 + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", 5930 + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", 4242 5931 "license": "BSD-3-Clause", 4243 5932 "dependencies": { 4244 5933 "side-channel": "^1.1.0" ··· 4256 5945 "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", 4257 5946 "license": "MIT" 4258 5947 }, 5948 + "node_modules/queue-microtask": { 5949 + "version": "1.2.3", 5950 + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 5951 + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 5952 + "dev": true, 5953 + "funding": [ 5954 + { 5955 + "type": "github", 5956 + "url": "https://github.com/sponsors/feross" 5957 + }, 5958 + { 5959 + "type": "patreon", 5960 + "url": "https://www.patreon.com/feross" 5961 + }, 5962 + { 5963 + "type": "consulting", 5964 + "url": "https://feross.org/support" 5965 + } 5966 + ], 5967 + "license": "MIT" 5968 + }, 4259 5969 "node_modules/range-parser": { 4260 5970 "version": "1.2.1", 4261 5971 "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", ··· 4281 5991 } 4282 5992 }, 4283 5993 "node_modules/raw-body/node_modules/iconv-lite": { 4284 - "version": "0.7.1", 4285 - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", 4286 - "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", 5994 + "version": "0.7.2", 5995 + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", 5996 + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", 4287 5997 "license": "MIT", 4288 5998 "dependencies": { 4289 5999 "safer-buffer": ">= 2.1.2 < 3.0.0" ··· 4311 6021 "rc": "cli.js" 4312 6022 } 4313 6023 }, 6024 + "node_modules/react": { 6025 + "version": "19.2.4", 6026 + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", 6027 + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", 6028 + "license": "MIT", 6029 + "engines": { 6030 + "node": ">=0.10.0" 6031 + } 6032 + }, 6033 + "node_modules/react-dom": { 6034 + "version": "19.2.4", 6035 + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", 6036 + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", 6037 + "license": "MIT", 6038 + "dependencies": { 6039 + "scheduler": "^0.27.0" 6040 + }, 6041 + "peerDependencies": { 6042 + "react": "^19.2.4" 6043 + } 6044 + }, 6045 + "node_modules/react-refresh": { 6046 + "version": "0.18.0", 6047 + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", 6048 + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", 6049 + "dev": true, 6050 + "license": "MIT", 6051 + "engines": { 6052 + "node": ">=0.10.0" 6053 + } 6054 + }, 6055 + "node_modules/read-cache": { 6056 + "version": "1.0.0", 6057 + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 6058 + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 6059 + "dev": true, 6060 + "license": "MIT", 6061 + "dependencies": { 6062 + "pify": "^2.3.0" 6063 + } 6064 + }, 4314 6065 "node_modules/readable-stream": { 4315 6066 "version": "3.6.2", 4316 6067 "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", ··· 4325 6076 "node": ">= 6" 4326 6077 } 4327 6078 }, 6079 + "node_modules/readdirp": { 6080 + "version": "3.6.0", 6081 + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 6082 + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 6083 + "dev": true, 6084 + "license": "MIT", 6085 + "dependencies": { 6086 + "picomatch": "^2.2.1" 6087 + }, 6088 + "engines": { 6089 + "node": ">=8.10.0" 6090 + } 6091 + }, 4328 6092 "node_modules/require-directory": { 4329 6093 "version": "2.1.1", 4330 6094 "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", ··· 4340 6104 "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", 4341 6105 "license": "MIT" 4342 6106 }, 6107 + "node_modules/resolve": { 6108 + "version": "1.22.11", 6109 + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", 6110 + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", 6111 + "dev": true, 6112 + "license": "MIT", 6113 + "dependencies": { 6114 + "is-core-module": "^2.16.1", 6115 + "path-parse": "^1.0.7", 6116 + "supports-preserve-symlinks-flag": "^1.0.0" 6117 + }, 6118 + "bin": { 6119 + "resolve": "bin/resolve" 6120 + }, 6121 + "engines": { 6122 + "node": ">= 0.4" 6123 + }, 6124 + "funding": { 6125 + "url": "https://github.com/sponsors/ljharb" 6126 + } 6127 + }, 4343 6128 "node_modules/resolve-pkg-maps": { 4344 6129 "version": "1.0.0", 4345 6130 "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", ··· 4350 6135 "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" 4351 6136 } 4352 6137 }, 6138 + "node_modules/reusify": { 6139 + "version": "1.1.0", 6140 + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", 6141 + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", 6142 + "dev": true, 6143 + "license": "MIT", 6144 + "engines": { 6145 + "iojs": ">=1.0.0", 6146 + "node": ">=0.10.0" 6147 + } 6148 + }, 6149 + "node_modules/rollup": { 6150 + "version": "4.57.1", 6151 + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", 6152 + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", 6153 + "dev": true, 6154 + "license": "MIT", 6155 + "dependencies": { 6156 + "@types/estree": "1.0.8" 6157 + }, 6158 + "bin": { 6159 + "rollup": "dist/bin/rollup" 6160 + }, 6161 + "engines": { 6162 + "node": ">=18.0.0", 6163 + "npm": ">=8.0.0" 6164 + }, 6165 + "optionalDependencies": { 6166 + "@rollup/rollup-android-arm-eabi": "4.57.1", 6167 + "@rollup/rollup-android-arm64": "4.57.1", 6168 + "@rollup/rollup-darwin-arm64": "4.57.1", 6169 + "@rollup/rollup-darwin-x64": "4.57.1", 6170 + "@rollup/rollup-freebsd-arm64": "4.57.1", 6171 + "@rollup/rollup-freebsd-x64": "4.57.1", 6172 + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", 6173 + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", 6174 + "@rollup/rollup-linux-arm64-gnu": "4.57.1", 6175 + "@rollup/rollup-linux-arm64-musl": "4.57.1", 6176 + "@rollup/rollup-linux-loong64-gnu": "4.57.1", 6177 + "@rollup/rollup-linux-loong64-musl": "4.57.1", 6178 + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", 6179 + "@rollup/rollup-linux-ppc64-musl": "4.57.1", 6180 + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", 6181 + "@rollup/rollup-linux-riscv64-musl": "4.57.1", 6182 + "@rollup/rollup-linux-s390x-gnu": "4.57.1", 6183 + "@rollup/rollup-linux-x64-gnu": "4.57.1", 6184 + "@rollup/rollup-linux-x64-musl": "4.57.1", 6185 + "@rollup/rollup-openbsd-x64": "4.57.1", 6186 + "@rollup/rollup-openharmony-arm64": "4.57.1", 6187 + "@rollup/rollup-win32-arm64-msvc": "4.57.1", 6188 + "@rollup/rollup-win32-ia32-msvc": "4.57.1", 6189 + "@rollup/rollup-win32-x64-gnu": "4.57.1", 6190 + "@rollup/rollup-win32-x64-msvc": "4.57.1", 6191 + "fsevents": "~2.3.2" 6192 + } 6193 + }, 4353 6194 "node_modules/router": { 4354 6195 "version": "2.2.0", 4355 6196 "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", ··· 4375 6216 "node": ">=0.12.0" 4376 6217 } 4377 6218 }, 6219 + "node_modules/run-parallel": { 6220 + "version": "1.2.0", 6221 + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 6222 + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 6223 + "dev": true, 6224 + "funding": [ 6225 + { 6226 + "type": "github", 6227 + "url": "https://github.com/sponsors/feross" 6228 + }, 6229 + { 6230 + "type": "patreon", 6231 + "url": "https://www.patreon.com/feross" 6232 + }, 6233 + { 6234 + "type": "consulting", 6235 + "url": "https://feross.org/support" 6236 + } 6237 + ], 6238 + "license": "MIT", 6239 + "dependencies": { 6240 + "queue-microtask": "^1.2.2" 6241 + } 6242 + }, 4378 6243 "node_modules/rxjs": { 4379 6244 "version": "7.8.2", 4380 6245 "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", ··· 4410 6275 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 4411 6276 "license": "MIT" 4412 6277 }, 6278 + "node_modules/scheduler": { 6279 + "version": "0.27.0", 6280 + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", 6281 + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", 6282 + "license": "MIT" 6283 + }, 4413 6284 "node_modules/semver": { 4414 - "version": "7.7.3", 4415 - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", 4416 - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", 6285 + "version": "6.3.1", 6286 + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", 6287 + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", 6288 + "dev": true, 4417 6289 "license": "ISC", 4418 6290 "bin": { 4419 6291 "semver": "bin/semver.js" 4420 - }, 4421 - "engines": { 4422 - "node": ">=10" 4423 6292 } 4424 6293 }, 4425 6294 "node_modules/send": { ··· 4540 6409 "@img/sharp-win32-x64": "0.34.5" 4541 6410 } 4542 6411 }, 6412 + "node_modules/sharp/node_modules/semver": { 6413 + "version": "7.7.4", 6414 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", 6415 + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", 6416 + "license": "ISC", 6417 + "bin": { 6418 + "semver": "bin/semver.js" 6419 + }, 6420 + "engines": { 6421 + "node": ">=10" 6422 + } 6423 + }, 4543 6424 "node_modules/side-channel": { 4544 6425 "version": "1.1.0", 4545 6426 "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", ··· 4717 6598 "node": ">=0.10.0" 4718 6599 } 4719 6600 }, 6601 + "node_modules/source-map-js": { 6602 + "version": "1.2.1", 6603 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 6604 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 6605 + "dev": true, 6606 + "license": "BSD-3-Clause", 6607 + "engines": { 6608 + "node": ">=0.10.0" 6609 + } 6610 + }, 4720 6611 "node_modules/statuses": { 4721 6612 "version": "2.0.2", 4722 6613 "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", ··· 4787 6678 "node": ">=0.10.0" 4788 6679 } 4789 6680 }, 6681 + "node_modules/sucrase": { 6682 + "version": "3.35.1", 6683 + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", 6684 + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", 6685 + "dev": true, 6686 + "license": "MIT", 6687 + "dependencies": { 6688 + "@jridgewell/gen-mapping": "^0.3.2", 6689 + "commander": "^4.0.0", 6690 + "lines-and-columns": "^1.1.6", 6691 + "mz": "^2.7.0", 6692 + "pirates": "^4.0.1", 6693 + "tinyglobby": "^0.2.11", 6694 + "ts-interface-checker": "^0.1.9" 6695 + }, 6696 + "bin": { 6697 + "sucrase": "bin/sucrase", 6698 + "sucrase-node": "bin/sucrase-node" 6699 + }, 6700 + "engines": { 6701 + "node": ">=16 || 14 >=14.17" 6702 + } 6703 + }, 6704 + "node_modules/sucrase/node_modules/commander": { 6705 + "version": "4.1.1", 6706 + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", 6707 + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", 6708 + "dev": true, 6709 + "license": "MIT", 6710 + "engines": { 6711 + "node": ">= 6" 6712 + } 6713 + }, 6714 + "node_modules/supports-preserve-symlinks-flag": { 6715 + "version": "1.0.0", 6716 + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 6717 + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 6718 + "dev": true, 6719 + "license": "MIT", 6720 + "engines": { 6721 + "node": ">= 0.4" 6722 + }, 6723 + "funding": { 6724 + "url": "https://github.com/sponsors/ljharb" 6725 + } 6726 + }, 6727 + "node_modules/tailwind-merge": { 6728 + "version": "3.4.0", 6729 + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", 6730 + "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", 6731 + "license": "MIT", 6732 + "funding": { 6733 + "type": "github", 6734 + "url": "https://github.com/sponsors/dcastil" 6735 + } 6736 + }, 6737 + "node_modules/tailwindcss": { 6738 + "version": "3.4.19", 6739 + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", 6740 + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", 6741 + "dev": true, 6742 + "license": "MIT", 6743 + "dependencies": { 6744 + "@alloc/quick-lru": "^5.2.0", 6745 + "arg": "^5.0.2", 6746 + "chokidar": "^3.6.0", 6747 + "didyoumean": "^1.2.2", 6748 + "dlv": "^1.1.3", 6749 + "fast-glob": "^3.3.2", 6750 + "glob-parent": "^6.0.2", 6751 + "is-glob": "^4.0.3", 6752 + "jiti": "^1.21.7", 6753 + "lilconfig": "^3.1.3", 6754 + "micromatch": "^4.0.8", 6755 + "normalize-path": "^3.0.0", 6756 + "object-hash": "^3.0.0", 6757 + "picocolors": "^1.1.1", 6758 + "postcss": "^8.4.47", 6759 + "postcss-import": "^15.1.0", 6760 + "postcss-js": "^4.0.1", 6761 + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", 6762 + "postcss-nested": "^6.2.0", 6763 + "postcss-selector-parser": "^6.1.2", 6764 + "resolve": "^1.22.8", 6765 + "sucrase": "^3.35.0" 6766 + }, 6767 + "bin": { 6768 + "tailwind": "lib/cli.js", 6769 + "tailwindcss": "lib/cli.js" 6770 + }, 6771 + "engines": { 6772 + "node": ">=14.0.0" 6773 + } 6774 + }, 4790 6775 "node_modules/tar-fs": { 4791 6776 "version": "2.1.4", 4792 6777 "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", ··· 4816 6801 } 4817 6802 }, 4818 6803 "node_modules/text-decoder": { 4819 - "version": "1.2.3", 4820 - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", 4821 - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", 6804 + "version": "1.2.6", 6805 + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.6.tgz", 6806 + "integrity": "sha512-27FeW5GQFDfw0FpwMQhMagB7BztOOlmjcSRi97t2oplhKVTZtp0DZbSegSaXS5IIC6mxMvBG4AR1Sgc6BX3CQg==", 4822 6807 "license": "Apache-2.0", 4823 6808 "dependencies": { 4824 6809 "b4a": "^1.6.4" 4825 6810 } 4826 6811 }, 6812 + "node_modules/thenify": { 6813 + "version": "3.3.1", 6814 + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", 6815 + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", 6816 + "dev": true, 6817 + "license": "MIT", 6818 + "dependencies": { 6819 + "any-promise": "^1.0.0" 6820 + } 6821 + }, 6822 + "node_modules/thenify-all": { 6823 + "version": "1.6.0", 6824 + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", 6825 + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", 6826 + "dev": true, 6827 + "license": "MIT", 6828 + "dependencies": { 6829 + "thenify": ">= 3.1.0 < 4" 6830 + }, 6831 + "engines": { 6832 + "node": ">=0.8" 6833 + } 6834 + }, 6835 + "node_modules/tinyglobby": { 6836 + "version": "0.2.15", 6837 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", 6838 + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 6839 + "dev": true, 6840 + "license": "MIT", 6841 + "dependencies": { 6842 + "fdir": "^6.5.0", 6843 + "picomatch": "^4.0.3" 6844 + }, 6845 + "engines": { 6846 + "node": ">=12.0.0" 6847 + }, 6848 + "funding": { 6849 + "url": "https://github.com/sponsors/SuperchupuDev" 6850 + } 6851 + }, 6852 + "node_modules/tinyglobby/node_modules/fdir": { 6853 + "version": "6.5.0", 6854 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 6855 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 6856 + "dev": true, 6857 + "license": "MIT", 6858 + "engines": { 6859 + "node": ">=12.0.0" 6860 + }, 6861 + "peerDependencies": { 6862 + "picomatch": "^3 || ^4" 6863 + }, 6864 + "peerDependenciesMeta": { 6865 + "picomatch": { 6866 + "optional": true 6867 + } 6868 + } 6869 + }, 6870 + "node_modules/tinyglobby/node_modules/picomatch": { 6871 + "version": "4.0.3", 6872 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 6873 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 6874 + "dev": true, 6875 + "license": "MIT", 6876 + "engines": { 6877 + "node": ">=12" 6878 + }, 6879 + "funding": { 6880 + "url": "https://github.com/sponsors/jonschlinkert" 6881 + } 6882 + }, 4827 6883 "node_modules/tlds": { 4828 6884 "version": "1.261.0", 4829 6885 "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.261.0.tgz", ··· 4831 6887 "license": "MIT", 4832 6888 "bin": { 4833 6889 "tlds": "bin.js" 6890 + } 6891 + }, 6892 + "node_modules/to-regex-range": { 6893 + "version": "5.0.1", 6894 + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 6895 + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 6896 + "dev": true, 6897 + "license": "MIT", 6898 + "dependencies": { 6899 + "is-number": "^7.0.0" 6900 + }, 6901 + "engines": { 6902 + "node": ">=8.0" 4834 6903 } 4835 6904 }, 4836 6905 "node_modules/toidentifier": { ··· 4877 6946 "url": "https://github.com/sponsors/wooorm" 4878 6947 } 4879 6948 }, 6949 + "node_modules/ts-interface-checker": { 6950 + "version": "0.1.13", 6951 + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", 6952 + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", 6953 + "dev": true, 6954 + "license": "Apache-2.0" 6955 + }, 4880 6956 "node_modules/tslib": { 4881 6957 "version": "2.8.1", 4882 6958 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", ··· 4965 7041 } 4966 7042 }, 4967 7043 "node_modules/undici": { 4968 - "version": "7.18.2", 4969 - "resolved": "https://registry.npmjs.org/undici/-/undici-7.18.2.tgz", 4970 - "integrity": "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==", 7044 + "version": "7.22.0", 7045 + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", 7046 + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", 4971 7047 "license": "MIT", 4972 7048 "engines": { 4973 7049 "node": ">=20.18.1" ··· 5004 7080 "node": ">= 0.8" 5005 7081 } 5006 7082 }, 7083 + "node_modules/update-browserslist-db": { 7084 + "version": "1.2.3", 7085 + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", 7086 + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", 7087 + "dev": true, 7088 + "funding": [ 7089 + { 7090 + "type": "opencollective", 7091 + "url": "https://opencollective.com/browserslist" 7092 + }, 7093 + { 7094 + "type": "tidelift", 7095 + "url": "https://tidelift.com/funding/github/npm/browserslist" 7096 + }, 7097 + { 7098 + "type": "github", 7099 + "url": "https://github.com/sponsors/ai" 7100 + } 7101 + ], 7102 + "license": "MIT", 7103 + "dependencies": { 7104 + "escalade": "^3.2.0", 7105 + "picocolors": "^1.1.1" 7106 + }, 7107 + "bin": { 7108 + "update-browserslist-db": "cli.js" 7109 + }, 7110 + "peerDependencies": { 7111 + "browserslist": ">= 4.21.0" 7112 + } 7113 + }, 5007 7114 "node_modules/url-parse": { 5008 7115 "version": "1.5.10", 5009 7116 "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", ··· 5029 7136 "node": ">= 0.8" 5030 7137 } 5031 7138 }, 7139 + "node_modules/vite": { 7140 + "version": "7.3.1", 7141 + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", 7142 + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", 7143 + "dev": true, 7144 + "license": "MIT", 7145 + "dependencies": { 7146 + "esbuild": "^0.27.0", 7147 + "fdir": "^6.5.0", 7148 + "picomatch": "^4.0.3", 7149 + "postcss": "^8.5.6", 7150 + "rollup": "^4.43.0", 7151 + "tinyglobby": "^0.2.15" 7152 + }, 7153 + "bin": { 7154 + "vite": "bin/vite.js" 7155 + }, 7156 + "engines": { 7157 + "node": "^20.19.0 || >=22.12.0" 7158 + }, 7159 + "funding": { 7160 + "url": "https://github.com/vitejs/vite?sponsor=1" 7161 + }, 7162 + "optionalDependencies": { 7163 + "fsevents": "~2.3.3" 7164 + }, 7165 + "peerDependencies": { 7166 + "@types/node": "^20.19.0 || >=22.12.0", 7167 + "jiti": ">=1.21.0", 7168 + "less": "^4.0.0", 7169 + "lightningcss": "^1.21.0", 7170 + "sass": "^1.70.0", 7171 + "sass-embedded": "^1.70.0", 7172 + "stylus": ">=0.54.8", 7173 + "sugarss": "^5.0.0", 7174 + "terser": "^5.16.0", 7175 + "tsx": "^4.8.1", 7176 + "yaml": "^2.4.2" 7177 + }, 7178 + "peerDependenciesMeta": { 7179 + "@types/node": { 7180 + "optional": true 7181 + }, 7182 + "jiti": { 7183 + "optional": true 7184 + }, 7185 + "less": { 7186 + "optional": true 7187 + }, 7188 + "lightningcss": { 7189 + "optional": true 7190 + }, 7191 + "sass": { 7192 + "optional": true 7193 + }, 7194 + "sass-embedded": { 7195 + "optional": true 7196 + }, 7197 + "stylus": { 7198 + "optional": true 7199 + }, 7200 + "sugarss": { 7201 + "optional": true 7202 + }, 7203 + "terser": { 7204 + "optional": true 7205 + }, 7206 + "tsx": { 7207 + "optional": true 7208 + }, 7209 + "yaml": { 7210 + "optional": true 7211 + } 7212 + } 7213 + }, 7214 + "node_modules/vite/node_modules/fdir": { 7215 + "version": "6.5.0", 7216 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 7217 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 7218 + "dev": true, 7219 + "license": "MIT", 7220 + "engines": { 7221 + "node": ">=12.0.0" 7222 + }, 7223 + "peerDependencies": { 7224 + "picomatch": "^3 || ^4" 7225 + }, 7226 + "peerDependenciesMeta": { 7227 + "picomatch": { 7228 + "optional": true 7229 + } 7230 + } 7231 + }, 7232 + "node_modules/vite/node_modules/picomatch": { 7233 + "version": "4.0.3", 7234 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 7235 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 7236 + "dev": true, 7237 + "license": "MIT", 7238 + "engines": { 7239 + "node": ">=12" 7240 + }, 7241 + "funding": { 7242 + "url": "https://github.com/sponsors/jonschlinkert" 7243 + } 7244 + }, 5032 7245 "node_modules/webdriver-bidi-protocol": { 5033 - "version": "0.3.10", 5034 - "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.10.tgz", 5035 - "integrity": "sha512-5LAE43jAVLOhB/QqX4bwSiv0Hg1HBfMmOuwBSXHdvg4GMGu9Y0lIq7p4R/yySu6w74WmaR4GM4H9t2IwLW7hgw==", 7246 + "version": "0.4.1", 7247 + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", 7248 + "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", 5036 7249 "license": "Apache-2.0" 5037 7250 }, 5038 7251 "node_modules/webidl-conversions": { ··· 5134 7347 "engines": { 5135 7348 "node": ">=10" 5136 7349 } 7350 + }, 7351 + "node_modules/yallist": { 7352 + "version": "3.1.1", 7353 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 7354 + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", 7355 + "dev": true, 7356 + "license": "ISC" 5137 7357 }, 5138 7358 "node_modules/yargs": { 5139 7359 "version": "17.7.2",
+24 -3
package.json
··· 5 5 "type": "module", 6 6 "main": "dist/index.js", 7 7 "scripts": { 8 - "build": "tsc", 8 + "setup": "npm install && npm run build", 9 + "build": "npm run build:server && npm run build:web", 10 + "build:server": "tsc", 11 + "build:web": "vite build", 12 + "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", 9 15 "start": "node dist/index.js", 10 16 "cli": "tsx src/cli.ts", 11 17 "dev": "tsx src/index.ts", 18 + "dev:web": "vite", 12 19 "import": "tsx src/index.ts --import-history", 13 20 "lint": "biome check --write .", 21 + "preview:web": "vite preview", 14 22 "format": "biome format --write .", 15 23 "typecheck": "tsc --noEmit" 16 24 }, ··· 32 40 "bcryptjs": "^3.0.3", 33 41 "better-sqlite3": "^12.5.0", 34 42 "cheerio": "^1.1.2", 43 + "class-variance-authority": "^0.7.1", 44 + "clsx": "^2.1.1", 35 45 "commander": "^14.0.2", 36 46 "cors": "^2.8.5", 37 47 "dotenv": "^17.2.3", ··· 40 50 "inquirer": "^13.1.0", 41 51 "iso-639-1": "^3.1.2", 42 52 "jsonwebtoken": "^9.0.3", 53 + "lucide-react": "^0.553.0", 43 54 "node-cron": "^4.2.1", 44 55 "puppeteer-core": "^24.34.0", 45 - "sharp": "^0.34.5" 56 + "react": "^19.2.0", 57 + "react-dom": "^19.2.0", 58 + "sharp": "^0.34.5", 59 + "tailwind-merge": "^3.3.1" 46 60 }, 47 61 "devDependencies": { 62 + "@types/react": "^19.2.6", 63 + "@types/react-dom": "^19.2.3", 64 + "@vitejs/plugin-react": "^5.1.0", 48 65 "@biomejs/biome": "^1.9.4", 49 66 "@types/bcryptjs": "^2.4.6", 50 67 "@types/better-sqlite3": "^7.6.13", ··· 55 72 "@types/jsonwebtoken": "^9.0.10", 56 73 "@types/node": "^22.10.2", 57 74 "@types/sharp": "^0.31.1", 75 + "autoprefixer": "^10.4.21", 76 + "postcss": "^8.5.6", 77 + "tailwindcss": "^3.4.18", 58 78 "tsx": "^4.19.2", 59 - "typescript": "^5.7.2" 79 + "typescript": "^5.7.2", 80 + "vite": "^7.1.12" 60 81 } 61 82 }
+6
postcss.config.cjs
··· 1 + module.exports = { 2 + plugins: { 3 + tailwindcss: { config: './web/tailwind.config.cjs' }, 4 + autoprefixer: {}, 5 + }, 6 + };
+22
scripts/rebuild-native.sh
··· 1 + #!/usr/bin/env bash 2 + 3 + set -euo pipefail 4 + 5 + echo "🔧 Rebuilding native modules for Node $(node -v)..." 6 + 7 + if npm rebuild better-sqlite3 >/dev/null 2>&1; then 8 + echo "✅ better-sqlite3 rebuilt successfully." 9 + exit 0 10 + fi 11 + 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." 15 + exit 0 16 + fi 17 + 18 + echo "❌ Failed to rebuild better-sqlite3 for this Node version." 19 + echo " macOS: run 'xcode-select --install'" 20 + echo " Debian/Ubuntu: sudo apt-get install -y build-essential python3 make g++" 21 + echo " Then run: npm run rebuild:native" 22 + exit 1
+423 -120
src/cli.ts
··· 1 + import { spawn } from 'node:child_process'; 2 + import fs from 'node:fs'; 3 + import path from 'node:path'; 4 + import { fileURLToPath } from 'node:url'; 1 5 import { Command } from 'commander'; 2 6 import inquirer from 'inquirer'; 3 - import { addMapping, getConfig, removeMapping, saveConfig, updateTwitterConfig } from './config-manager.js'; 7 + import { deleteAllPosts } from './bsky.js'; 8 + import { 9 + addMapping, 10 + getConfig, 11 + removeMapping, 12 + saveConfig, 13 + updateTwitterConfig, 14 + type AccountMapping, 15 + type AppConfig, 16 + } from './config-manager.js'; 17 + import { dbService } from './db.js'; 18 + 19 + const __filename = fileURLToPath(import.meta.url); 20 + const __dirname = path.dirname(__filename); 21 + const ROOT_DIR = path.join(__dirname, '..'); 22 + 23 + const normalizeHandle = (value: string) => value.trim().replace(/^@/, '').toLowerCase(); 24 + 25 + const parsePositiveInt = (value: string, defaultValue: number): number => { 26 + const parsed = Number.parseInt(value, 10); 27 + if (!Number.isFinite(parsed) || parsed <= 0) { 28 + return defaultValue; 29 + } 30 + return parsed; 31 + }; 32 + 33 + const findMappingByRef = (config: AppConfig, ref: string): AccountMapping | undefined => { 34 + const needle = normalizeHandle(ref); 35 + return config.mappings.find( 36 + (mapping) => 37 + mapping.id === ref || 38 + normalizeHandle(mapping.bskyIdentifier) === needle || 39 + mapping.twitterUsernames.some((username) => normalizeHandle(username) === needle), 40 + ); 41 + }; 42 + 43 + const selectMapping = async (message: string): Promise<AccountMapping | null> => { 44 + const config = getConfig(); 45 + if (config.mappings.length === 0) { 46 + console.log('No mappings found.'); 47 + return null; 48 + } 49 + 50 + const { id } = await inquirer.prompt([ 51 + { 52 + type: 'list', 53 + name: 'id', 54 + message, 55 + choices: config.mappings.map((mapping) => ({ 56 + name: `${mapping.owner || 'System'}: ${mapping.twitterUsernames.join(', ')} -> ${mapping.bskyIdentifier}`, 57 + value: mapping.id, 58 + })), 59 + }, 60 + ]); 61 + 62 + return config.mappings.find((mapping) => mapping.id === id) ?? null; 63 + }; 64 + 65 + const spawnAndWait = async (command: string, args: string[], cwd: string): Promise<void> => 66 + new Promise((resolve, reject) => { 67 + const child = spawn(command, args, { 68 + cwd, 69 + stdio: 'inherit', 70 + env: process.env, 71 + }); 72 + 73 + child.on('error', reject); 74 + child.on('exit', (code) => { 75 + if (code === 0) { 76 + resolve(); 77 + return; 78 + } 79 + reject(new Error(`Process exited with code ${code}`)); 80 + }); 81 + }); 82 + 83 + const runCoreCommand = async (args: string[]): Promise<void> => { 84 + const distEntry = path.join(ROOT_DIR, 'dist', 'index.js'); 85 + if (fs.existsSync(distEntry)) { 86 + await spawnAndWait(process.execPath, [distEntry, ...args], ROOT_DIR); 87 + return; 88 + } 89 + 90 + const tsxBin = 91 + process.platform === 'win32' 92 + ? path.join(ROOT_DIR, 'node_modules', '.bin', 'tsx.cmd') 93 + : path.join(ROOT_DIR, 'node_modules', '.bin', 'tsx'); 94 + 95 + const sourceEntry = path.join(ROOT_DIR, 'src', 'index.ts'); 96 + if (fs.existsSync(tsxBin) && fs.existsSync(sourceEntry)) { 97 + await spawnAndWait(tsxBin, [sourceEntry, ...args], ROOT_DIR); 98 + return; 99 + } 100 + 101 + throw new Error('Could not find dist/index.js or tsx runtime. Run npm run build first.'); 102 + }; 103 + 104 + const ensureMapping = async (mappingRef?: string): Promise<AccountMapping | null> => { 105 + const config = getConfig(); 106 + if (config.mappings.length === 0) { 107 + console.log('No mappings found.'); 108 + return null; 109 + } 110 + 111 + if (mappingRef) { 112 + const mapping = findMappingByRef(config, mappingRef); 113 + if (!mapping) { 114 + console.log(`No mapping found for '${mappingRef}'.`); 115 + return null; 116 + } 117 + return mapping; 118 + } 119 + 120 + return selectMapping('Select a mapping:'); 121 + }; 122 + 123 + const exportConfig = (outputFile: string) => { 124 + const config = getConfig(); 125 + const { users, ...cleanConfig } = config; 126 + const outputPath = path.resolve(outputFile); 127 + fs.writeFileSync(outputPath, JSON.stringify(cleanConfig, null, 2)); 128 + console.log(`Exported config to ${outputPath}.`); 129 + }; 130 + 131 + const importConfig = (inputFile: string) => { 132 + const inputPath = path.resolve(inputFile); 133 + if (!fs.existsSync(inputPath)) { 134 + throw new Error(`File not found: ${inputPath}`); 135 + } 136 + 137 + const parsed = JSON.parse(fs.readFileSync(inputPath, 'utf8')); 138 + if (!parsed.mappings || !Array.isArray(parsed.mappings)) { 139 + throw new Error('Invalid config format: missing mappings array.'); 140 + } 141 + 142 + const currentConfig = getConfig(); 143 + const nextConfig: AppConfig = { 144 + ...currentConfig, 145 + mappings: parsed.mappings, 146 + twitter: parsed.twitter || currentConfig.twitter, 147 + ai: parsed.ai || currentConfig.ai, 148 + checkIntervalMinutes: parsed.checkIntervalMinutes || currentConfig.checkIntervalMinutes, 149 + }; 150 + 151 + saveConfig(nextConfig); 152 + console.log('Config imported successfully. Existing users were preserved.'); 153 + }; 4 154 5 155 const program = new Command(); 6 156 7 157 program 8 158 .name('tweets-2-bsky-cli') 9 - .description('CLI to manage Twitter to Bluesky crossposting mappings') 10 - .version('1.0.0'); 159 + .description('CLI for full Tweets -> Bluesky dashboard workflows') 160 + .version('2.1.0'); 11 161 12 162 program 13 163 .command('setup-ai') ··· 16 166 const config = getConfig(); 17 167 const currentAi = config.ai || { provider: 'gemini' }; 18 168 19 - // Check legacy gemini key if not in new config 20 169 if (!config.ai && config.geminiApiKey) { 21 170 currentAi.apiKey = config.geminiApiKey; 22 171 } ··· 39 188 name: 'apiKey', 40 189 message: 'Enter API Key (optional for some custom providers):', 41 190 default: currentAi.apiKey, 42 - validate: (input: string, answers: any) => { 43 - if (['gemini', 'anthropic'].includes(answers.provider) && !input) { 44 - return 'API Key is required for this provider.'; 45 - } 46 - return true; 47 - }, 48 191 }, 49 192 { 50 193 type: 'input', ··· 55 198 { 56 199 type: 'input', 57 200 name: 'baseUrl', 58 - message: 'Enter Base URL (optional, e.g. for OpenRouter):', 201 + message: 'Enter Base URL (optional):', 59 202 default: currentAi.baseUrl, 60 203 when: (answers) => ['openai', 'anthropic', 'custom'].includes(answers.provider), 61 204 }, ··· 68 211 baseUrl: answers.baseUrl || undefined, 69 212 }; 70 213 71 - // Clear legacy key to avoid confusion 72 214 delete config.geminiApiKey; 73 - 74 215 saveConfig(config); 75 - console.log('AI configuration updated!'); 216 + console.log('AI configuration updated.'); 76 217 }); 77 218 78 219 program 79 220 .command('setup-twitter') 80 - .description('Setup Twitter auth cookies') 221 + .description('Setup Twitter auth cookies (primary + backup)') 81 222 .action(async () => { 82 223 const config = getConfig(); 83 224 const answers = await inquirer.prompt([ 84 225 { 85 226 type: 'input', 86 227 name: 'authToken', 87 - message: 'Enter Twitter auth_token:', 228 + message: 'Primary Twitter auth_token:', 88 229 default: config.twitter.authToken, 89 230 }, 90 231 { 91 232 type: 'input', 92 233 name: 'ct0', 93 - message: 'Enter Twitter ct0:', 234 + message: 'Primary Twitter ct0:', 94 235 default: config.twitter.ct0, 236 + }, 237 + { 238 + type: 'input', 239 + name: 'backupAuthToken', 240 + message: 'Backup Twitter auth_token (optional):', 241 + default: config.twitter.backupAuthToken, 242 + }, 243 + { 244 + type: 'input', 245 + name: 'backupCt0', 246 + message: 'Backup Twitter ct0 (optional):', 247 + default: config.twitter.backupCt0, 95 248 }, 96 249 ]); 250 + 97 251 updateTwitterConfig(answers); 98 - console.log('Twitter config updated!'); 252 + console.log('Twitter credentials updated.'); 99 253 }); 100 254 101 255 program 102 256 .command('add-mapping') 103 - .description('Add a new Twitter to Bluesky mapping') 257 + .description('Add a new Twitter -> Bluesky mapping') 104 258 .action(async () => { 105 259 const answers = await inquirer.prompt([ 260 + { 261 + type: 'input', 262 + name: 'owner', 263 + message: 'Owner name:', 264 + }, 106 265 { 107 266 type: 'input', 108 267 name: 'twitterUsernames', ··· 128 287 129 288 const usernames = answers.twitterUsernames 130 289 .split(',') 131 - .map((u: string) => u.trim()) 132 - .filter((u: string) => u.length > 0); 290 + .map((username: string) => username.trim()) 291 + .filter((username: string) => username.length > 0); 133 292 134 293 addMapping({ 135 - ...answers, 294 + owner: answers.owner, 136 295 twitterUsernames: usernames, 296 + bskyIdentifier: answers.bskyIdentifier, 297 + bskyPassword: answers.bskyPassword, 298 + bskyServiceUrl: answers.bskyServiceUrl, 137 299 }); 138 - console.log('Mapping added successfully!'); 300 + console.log('Mapping added successfully.'); 139 301 }); 140 302 141 303 program 142 - .command('edit-mapping') 143 - .description('Edit an existing mapping') 144 - .action(async () => { 145 - const config = getConfig(); 146 - if (config.mappings.length === 0) { 147 - console.log('No mappings found.'); 148 - return; 149 - } 150 - 151 - const { id } = await inquirer.prompt([ 152 - { 153 - type: 'list', 154 - name: 'id', 155 - message: 'Select a mapping to edit:', 156 - choices: config.mappings.map((m) => ({ 157 - name: `${m.twitterUsernames.join(', ')} -> ${m.bskyIdentifier}`, 158 - value: m.id, 159 - })), 160 - }, 161 - ]); 162 - 163 - const mapping = config.mappings.find((m) => m.id === id); 304 + .command('edit-mapping [mapping]') 305 + .description('Edit a mapping by id/handle/twitter username') 306 + .action(async (mappingRef?: string) => { 307 + const mapping = await ensureMapping(mappingRef); 164 308 if (!mapping) return; 165 309 310 + const config = getConfig(); 166 311 const answers = await inquirer.prompt([ 167 312 { 168 313 type: 'input', 314 + name: 'owner', 315 + message: 'Owner:', 316 + default: mapping.owner || '', 317 + }, 318 + { 319 + type: 'input', 169 320 name: 'twitterUsernames', 170 321 message: 'Twitter username(s) (comma separated):', 171 322 default: mapping.twitterUsernames.join(', '), ··· 191 342 192 343 const usernames = answers.twitterUsernames 193 344 .split(',') 194 - .map((u: string) => u.trim()) 195 - .filter((u: string) => u.length > 0); 345 + .map((username: string) => username.trim()) 346 + .filter((username: string) => username.length > 0); 196 347 197 - // Update the mapping directly 198 - const index = config.mappings.findIndex((m) => m.id === id); 348 + const index = config.mappings.findIndex((entry) => entry.id === mapping.id); 349 + if (index === -1) return; 350 + 199 351 const existingMapping = config.mappings[index]; 352 + if (!existingMapping) return; 200 353 201 - if (index !== -1 && existingMapping) { 202 - const updatedMapping = { 203 - ...existingMapping, 204 - twitterUsernames: usernames, 205 - bskyIdentifier: answers.bskyIdentifier, 206 - bskyServiceUrl: answers.bskyServiceUrl, 207 - }; 354 + const updatedMapping = { 355 + ...existingMapping, 356 + owner: answers.owner, 357 + twitterUsernames: usernames, 358 + bskyIdentifier: answers.bskyIdentifier, 359 + bskyServiceUrl: answers.bskyServiceUrl, 360 + }; 208 361 209 - if (answers.bskyPassword && answers.bskyPassword.trim().length > 0) { 210 - updatedMapping.bskyPassword = answers.bskyPassword; 211 - } 362 + if (answers.bskyPassword && answers.bskyPassword.trim().length > 0) { 363 + updatedMapping.bskyPassword = answers.bskyPassword; 364 + } 212 365 213 - config.mappings[index] = updatedMapping; 214 - saveConfig(config); 215 - console.log('Mapping updated successfully!'); 216 - } 366 + config.mappings[index] = updatedMapping; 367 + saveConfig(config); 368 + console.log('Mapping updated successfully.'); 217 369 }); 218 370 219 371 program ··· 225 377 console.log('No mappings found.'); 226 378 return; 227 379 } 380 + 228 381 console.table( 229 - config.mappings.map((m) => ({ 230 - id: m.id, 231 - twitter: m.twitterUsernames.join(', '), 232 - bsky: m.bskyIdentifier, 233 - enabled: m.enabled, 382 + config.mappings.map((mapping) => ({ 383 + id: mapping.id, 384 + owner: mapping.owner || 'System', 385 + twitter: mapping.twitterUsernames.join(', '), 386 + bsky: mapping.bskyIdentifier, 387 + enabled: mapping.enabled, 234 388 })), 235 389 ); 236 390 }); 237 391 238 392 program 239 - .command('remove') 240 - .description('Remove a mapping') 241 - .action(async () => { 242 - const config = getConfig(); 243 - if (config.mappings.length === 0) { 244 - console.log('No mappings to remove.'); 245 - return; 246 - } 247 - const { id } = await inquirer.prompt([ 393 + .command('remove [mapping]') 394 + .description('Remove a mapping by id/handle/twitter username') 395 + .action(async (mappingRef?: string) => { 396 + const mapping = await ensureMapping(mappingRef); 397 + if (!mapping) return; 398 + 399 + const { confirmed } = await inquirer.prompt([ 248 400 { 249 - type: 'list', 250 - name: 'id', 251 - message: 'Select a mapping to remove:', 252 - choices: config.mappings.map((m) => ({ 253 - name: `${m.twitterUsernames.join(', ')} -> ${m.bskyIdentifier}`, 254 - value: m.id, 255 - })), 401 + type: 'confirm', 402 + name: 'confirmed', 403 + message: `Remove mapping ${mapping.twitterUsernames.join(', ')} -> ${mapping.bskyIdentifier}?`, 404 + default: false, 256 405 }, 257 406 ]); 258 - removeMapping(id); 407 + 408 + if (!confirmed) { 409 + console.log('Cancelled.'); 410 + return; 411 + } 412 + 413 + removeMapping(mapping.id); 259 414 console.log('Mapping removed.'); 260 415 }); 261 416 262 417 program 263 - .command('import-history') 264 - .description('Import history for a specific mapping') 265 - .action(async () => { 418 + .command('import-history [mapping]') 419 + .description('Import history immediately for one mapping') 420 + .option('-l, --limit <number>', 'Tweet limit', '15') 421 + .option('--dry-run', 'Do not post to Bluesky', false) 422 + .option('--web', 'Keep web server enabled during import', false) 423 + .action(async (mappingRef: string | undefined, options) => { 424 + const mapping = await ensureMapping(mappingRef); 425 + if (!mapping) return; 426 + 427 + let username = mapping.twitterUsernames[0] ?? ''; 428 + if (!username) { 429 + console.log('Mapping has no Twitter usernames.'); 430 + return; 431 + } 432 + 433 + if (mapping.twitterUsernames.length > 1) { 434 + const answer = await inquirer.prompt([ 435 + { 436 + type: 'list', 437 + name: 'username', 438 + message: 'Select Twitter username to import:', 439 + choices: mapping.twitterUsernames, 440 + default: username, 441 + }, 442 + ]); 443 + username = String(answer.username || '').trim(); 444 + } 445 + 446 + const args: string[] = [ 447 + '--import-history', 448 + '--username', 449 + username, 450 + '--limit', 451 + String(parsePositiveInt(options.limit, 15)), 452 + ]; 453 + if (options.dryRun) args.push('--dry-run'); 454 + if (!options.web) args.push('--no-web'); 455 + 456 + await runCoreCommand(args); 457 + }); 458 + 459 + program 460 + .command('set-interval <minutes>') 461 + .description('Set scheduler interval in minutes') 462 + .action((minutes) => { 463 + const parsed = parsePositiveInt(minutes, 5); 266 464 const config = getConfig(); 267 - if (config.mappings.length === 0) { 268 - console.log('No mappings found.'); 465 + config.checkIntervalMinutes = parsed; 466 + saveConfig(config); 467 + console.log(`Interval set to ${parsed} minutes.`); 468 + }); 469 + 470 + program 471 + .command('run-now') 472 + .description('Run one sync cycle now (ideal for cronjobs)') 473 + .option('--dry-run', 'Fetch but do not post', false) 474 + .option('--web', 'Keep web server enabled during this run', false) 475 + .action(async (options) => { 476 + const args = ['--run-once']; 477 + if (options.dryRun) args.push('--dry-run'); 478 + if (!options.web) args.push('--no-web'); 479 + await runCoreCommand(args); 480 + }); 481 + 482 + program 483 + .command('backfill [mapping]') 484 + .description('Run backfill now for one mapping (id/handle/twitter username)') 485 + .option('-l, --limit <number>', 'Tweet limit', '15') 486 + .option('--dry-run', 'Fetch but do not post', false) 487 + .option('--web', 'Keep web server enabled during this run', false) 488 + .action(async (mappingRef: string | undefined, options) => { 489 + const mapping = await ensureMapping(mappingRef); 490 + if (!mapping) return; 491 + 492 + const args = ['--run-once', '--backfill-mapping', mapping.id, '--backfill-limit', String(parsePositiveInt(options.limit, 15))]; 493 + if (options.dryRun) args.push('--dry-run'); 494 + if (!options.web) args.push('--no-web'); 495 + 496 + await runCoreCommand(args); 497 + }); 498 + 499 + program 500 + .command('clear-cache [mapping]') 501 + .description('Clear cached tweet history for a mapping') 502 + .action(async (mappingRef?: string) => { 503 + const mapping = await ensureMapping(mappingRef); 504 + if (!mapping) return; 505 + 506 + for (const username of mapping.twitterUsernames) { 507 + dbService.deleteTweetsByUsername(username); 508 + } 509 + 510 + console.log(`Cache cleared for ${mapping.twitterUsernames.join(', ')}.`); 511 + }); 512 + 513 + program 514 + .command('delete-all-posts [mapping]') 515 + .description('Delete all posts on mapped Bluesky account and clear local cache') 516 + .action(async (mappingRef?: string) => { 517 + const mapping = await ensureMapping(mappingRef); 518 + if (!mapping) return; 519 + 520 + const { confirmed } = await inquirer.prompt([ 521 + { 522 + type: 'confirm', 523 + name: 'confirmed', 524 + message: `Delete ALL posts for ${mapping.bskyIdentifier}? This cannot be undone.`, 525 + default: false, 526 + }, 527 + ]); 528 + 529 + if (!confirmed) { 530 + console.log('Cancelled.'); 269 531 return; 270 532 } 271 - const { id } = await inquirer.prompt([ 533 + 534 + const { typed } = await inquirer.prompt([ 272 535 { 273 - type: 'list', 274 - name: 'id', 275 - message: 'Select a mapping to import history for:', 276 - choices: config.mappings.map((m) => ({ 277 - name: `${m.twitterUsernames.join(', ')} -> ${m.bskyIdentifier}`, 278 - value: m.id, 279 - })), 536 + type: 'input', 537 + name: 'typed', 538 + message: 'Type DELETE to confirm:', 280 539 }, 281 540 ]); 282 541 283 - const mapping = config.mappings.find((m) => m.id === id); 284 - if (!mapping) return; 542 + if (typed !== 'DELETE') { 543 + console.log('Confirmation failed. Aborting.'); 544 + return; 545 + } 285 546 286 - console.log(` 287 - To import history, run one of the following commands:`); 288 - for (const username of mapping.twitterUsernames) { 289 - console.log(` npm run import -- --username ${username}`); 547 + const deleted = await deleteAllPosts(mapping.id); 548 + dbService.deleteTweetsByBskyIdentifier(mapping.bskyIdentifier); 549 + console.log(`Deleted ${deleted} posts for ${mapping.bskyIdentifier} and cleared local cache.`); 550 + }); 551 + 552 + program 553 + .command('recent-activity') 554 + .description('Show recent processed tweets') 555 + .option('-l, --limit <number>', 'Number of rows', '20') 556 + .action((options) => { 557 + const limit = parsePositiveInt(options.limit, 20); 558 + const rows = dbService.getRecentProcessedTweets(limit); 559 + 560 + if (rows.length === 0) { 561 + console.log('No recent activity found.'); 562 + return; 290 563 } 291 - console.log(` 292 - You can also use additional flags:`); 293 - console.log(' --limit <number> Limit the number of tweets to import'); 294 - console.log(' --dry-run Fetch and show tweets without posting'); 295 - console.log(` 296 - Example:`); 297 - console.log(` npm run import -- --username ${mapping.twitterUsernames[0]} --limit 10 --dry-run 298 - `); 564 + 565 + console.table( 566 + rows.map((row) => ({ 567 + time: row.created_at, 568 + twitter: row.twitter_username, 569 + bsky: row.bsky_identifier, 570 + status: row.status, 571 + text: row.tweet_text ? row.tweet_text.slice(0, 80) : row.twitter_id, 572 + })), 573 + ); 574 + }); 575 + 576 + program 577 + .command('config-export [file]') 578 + .description('Export dashboard config (without users/password hashes)') 579 + .action((file = 'tweets-2-bsky-config.json') => { 580 + exportConfig(file); 581 + }); 582 + 583 + program 584 + .command('config-import <file>') 585 + .description('Import dashboard config (preserves existing users)') 586 + .action((file) => { 587 + importConfig(file); 299 588 }); 300 589 301 590 program 302 - .command('set-interval') 303 - .description('Set check interval in minutes') 304 - .argument('<minutes>', 'Interval in minutes') 305 - .action((minutes) => { 591 + .command('status') 592 + .description('Show local CLI status summary') 593 + .action(() => { 306 594 const config = getConfig(); 307 - config.checkIntervalMinutes = Number.parseInt(minutes, 10); 308 - saveConfig(config); 309 - console.log(`Interval set to ${minutes} minutes.`); 595 + const recent = dbService.getRecentProcessedTweets(5); 596 + 597 + console.log('Tweets-2-Bsky status'); 598 + console.log('--------------------'); 599 + console.log(`Mappings: ${config.mappings.length}`); 600 + console.log(`Enabled mappings: ${config.mappings.filter((mapping) => mapping.enabled).length}`); 601 + console.log(`Check interval: ${config.checkIntervalMinutes} minute(s)`); 602 + console.log(`Twitter configured: ${Boolean(config.twitter.authToken && config.twitter.ct0)}`); 603 + console.log(`AI provider: ${config.ai?.provider || 'gemini (default)'}`); 604 + console.log(`Recent processed tweets: ${recent.length > 0 ? recent.length : 0}`); 605 + 606 + if (recent.length > 0) { 607 + const last = recent[0]; 608 + console.log(`Latest activity: ${last?.created_at || 'unknown'} (${last?.status || 'unknown'})`); 609 + } 310 610 }); 311 611 312 - program.parse(); 612 + program.parseAsync().catch((error) => { 613 + console.error(error instanceof Error ? error.message : error); 614 + process.exit(1); 615 + });
+88 -13
src/index.ts
··· 237 237 let scraper: Scraper | null = null; 238 238 let currentTwitterCookies = { authToken: '', ct0: '' }; 239 239 let useBackupCredentials = false; 240 + const lastCreatedAtByBsky = new Map<string, number>(); 241 + 242 + function getUniqueCreatedAtIso(bskyIdentifier: string, desiredMs: number): string { 243 + const key = bskyIdentifier.toLowerCase(); 244 + const lastMs = lastCreatedAtByBsky.get(key) ?? Number.MIN_SAFE_INTEGER; 245 + const nextMs = Math.max(desiredMs, lastMs + 1); 246 + lastCreatedAtByBsky.set(key, nextMs); 247 + return new Date(nextMs).toISOString(); 248 + } 240 249 241 250 async function getTwitterScraper(forceReset = false): Promise<Scraper | null> { 242 251 const config = getConfig(); ··· 1792 1801 rt.facets = addTwitterHandleLinkFacets(rt.text, rt.facets); 1793 1802 const detectedLangs = detectLanguage(chunk); 1794 1803 1795 - // Add offset for split chunks to ensure correct ordering/threading 1796 - let postDate = tweet.created_at ? new Date(tweet.created_at) : new Date(); 1797 - if (i > 0) { 1798 - postDate = new Date(postDate.getTime() + i * 1000); 1799 - } 1804 + // Preserve original timing when available, but enforce monotonic per-account 1805 + // timestamps to avoid equal-createdAt collisions in fast self-thread replies. 1806 + const parsedCreatedAt = tweet.created_at ? Date.parse(tweet.created_at) : NaN; 1807 + const baseCreatedAtMs = Number.isFinite(parsedCreatedAt) ? parsedCreatedAt : Date.now(); 1808 + const chunkCreatedAtMs = baseCreatedAtMs + i * 1000; 1800 1809 1801 1810 // biome-ignore lint/suspicious/noExplicitAny: dynamic record construction 1802 1811 const postRecord: Record<string, any> = { 1803 1812 text: rt.text, 1804 1813 facets: rt.facets, 1805 1814 langs: detectedLangs, 1806 - createdAt: postDate.toISOString(), 1815 + // CID is generated by the PDS from record content; unique createdAt keeps 1816 + // near-simultaneous self-thread posts from colliding on identical payloads. 1817 + createdAt: getUniqueCreatedAtIso(bskyIdentifier, chunkCreatedAtMs), 1807 1818 }; 1808 1819 1809 1820 if (i === 0) { ··· 2026 2037 if (!agent) return; 2027 2038 2028 2039 const backfillReq = backfillRequest ?? getPendingBackfills().find((b) => b.id === mapping.id); 2040 + const explicitBackfill = Boolean(backfillRequest); 2029 2041 2030 2042 if (backfillReq) { 2031 2043 const limit = backfillReq.limit || 15; ··· 2041 2053 }); 2042 2054 2043 2055 for (const twitterUsername of mapping.twitterUsernames) { 2044 - const stillPending = getPendingBackfills().some( 2045 - (b) => b.id === mapping.id && b.requestId === backfillReq.requestId, 2046 - ); 2056 + const stillPending = explicitBackfill 2057 + ? true 2058 + : getPendingBackfills().some((b) => b.id === mapping.id && b.requestId === backfillReq.requestId); 2047 2059 if (!stillPending) { 2048 2060 console.log(`[${mapping.bskyIdentifier}] 🛑 Backfill request replaced; stopping.`); 2049 2061 break; ··· 2134 2146 .description('Crosspost tweets to Bluesky') 2135 2147 .option('--dry-run', 'Fetch tweets but do not post to Bluesky', false) 2136 2148 .option('--no-web', 'Disable the web interface') 2149 + .option('--run-once', 'Run one check cycle immediately and exit', false) 2150 + .option('--backfill-mapping <mapping>', 'Run backfill now for a mapping id/handle/twitter username') 2151 + .option('--backfill-limit <number>', 'Limit for --backfill-mapping', (val) => Number.parseInt(val, 10)) 2137 2152 .option('--import-history', 'Run in history import mode') 2138 2153 .option('--username <username>', 'Twitter username for history import') 2139 2154 .option('--limit <number>', 'Limit the number of tweets to import', (val) => Number.parseInt(val, 10)) ··· 2176 2191 process.exit(0); 2177 2192 } 2178 2193 2179 - if (options.dryRun) { 2180 - console.log('Dry run complete. Exiting.'); 2194 + const findMappingById = (mappings: AccountMapping[], id: string) => mappings.find((mapping) => mapping.id === id); 2195 + const normalizeHandle = (value: string) => value.trim().replace(/^@/, '').toLowerCase(); 2196 + const findMappingByRef = (mappings: AccountMapping[], ref: string) => { 2197 + const needle = normalizeHandle(ref); 2198 + return mappings.find( 2199 + (mapping) => 2200 + mapping.id === ref || 2201 + normalizeHandle(mapping.bskyIdentifier) === needle || 2202 + mapping.twitterUsernames.some((username) => normalizeHandle(username) === needle), 2203 + ); 2204 + }; 2205 + 2206 + const runSingleCycle = async (cycleConfig: ReturnType<typeof getConfig>) => { 2207 + const runLimit = pLimit(3); 2208 + const tasks: Promise<void>[] = []; 2209 + 2210 + if (options.backfillMapping) { 2211 + const mapping = findMappingByRef(cycleConfig.mappings, options.backfillMapping); 2212 + if (!mapping) { 2213 + console.error(`No mapping found for '${options.backfillMapping}'.`); 2214 + process.exit(1); 2215 + } 2216 + if (!mapping.enabled) { 2217 + console.error(`Mapping '${mapping.bskyIdentifier}' is disabled.`); 2218 + process.exit(1); 2219 + } 2220 + 2221 + const requestId = `cli-${Date.now()}`; 2222 + const backfillRequest: PendingBackfill = { 2223 + id: mapping.id, 2224 + limit: options.backfillLimit || options.limit || 15, 2225 + queuedAt: Date.now(), 2226 + sequence: 0, 2227 + requestId, 2228 + }; 2229 + 2230 + console.log(`[CLI] 🚧 Running backfill for ${mapping.bskyIdentifier}...`); 2231 + await runAccountTask(mapping, backfillRequest, options.dryRun); 2232 + updateAppStatus({ state: 'idle', message: `Backfill complete for ${mapping.bskyIdentifier}` }); 2233 + return; 2234 + } 2235 + 2236 + for (const mapping of cycleConfig.mappings) { 2237 + if (!mapping.enabled) continue; 2238 + tasks.push( 2239 + runLimit(async () => { 2240 + await runAccountTask(mapping, undefined, options.dryRun); 2241 + }), 2242 + ); 2243 + } 2244 + 2245 + if (tasks.length === 0) { 2246 + console.log('[CLI] No enabled mappings found.'); 2247 + updateAppStatus({ state: 'idle', message: 'No enabled mappings found' }); 2248 + return; 2249 + } 2250 + 2251 + await Promise.all(tasks); 2252 + updateAppStatus({ state: 'idle', message: options.dryRun ? 'Dry run cycle complete' : 'Run-once cycle complete' }); 2253 + }; 2254 + 2255 + if (options.runOnce || options.backfillMapping || options.dryRun) { 2256 + await runSingleCycle(getConfig()); 2257 + console.log(options.dryRun ? 'Dry run cycle complete. Exiting.' : 'Run-once cycle complete. Exiting.'); 2181 2258 process.exit(0); 2182 2259 } 2183 2260 ··· 2186 2263 2187 2264 // Concurrency limit for processing accounts 2188 2265 const runLimit = pLimit(3); 2189 - 2190 - const findMappingById = (mappings: AccountMapping[], id: string) => mappings.find((mapping) => mapping.id === id); 2191 2266 2192 2267 // Main loop 2193 2268 while (true) {
+6 -2
src/server.ts
··· 1 + import fs from 'node:fs'; 1 2 import path from 'node:path'; 2 3 import { fileURLToPath } from 'node:url'; 3 4 import bcrypt from 'bcryptjs'; ··· 14 15 const app = express(); 15 16 const PORT = Number(process.env.PORT) || 3000; 16 17 const JWT_SECRET = process.env.JWT_SECRET || 'fallback-secret'; 18 + const WEB_DIST_DIR = path.join(__dirname, '..', 'web', 'dist'); 19 + const LEGACY_PUBLIC_DIR = path.join(__dirname, '..', 'public'); 20 + const staticAssetsDir = fs.existsSync(path.join(WEB_DIST_DIR, 'index.html')) ? WEB_DIST_DIR : LEGACY_PUBLIC_DIR; 17 21 18 22 // In-memory state for triggers and scheduling 19 23 let lastCheckTime = Date.now(); ··· 47 51 app.use(cors()); 48 52 app.use(express.json()); 49 53 50 - app.use(express.static(path.join(__dirname, '../public'))); 54 + app.use(express.static(staticAssetsDir)); 51 55 52 56 // Middleware to protect routes 53 57 const authenticateToken = (req: any, res: any, next: any) => { ··· 440 444 441 445 // Serve the frontend for any other route (middleware approach for Express 5) 442 446 app.use((_req, res) => { 443 - res.sendFile(path.join(__dirname, '../public/index.html')); 447 + res.sendFile(path.join(staticAssetsDir, 'index.html')); 444 448 }); 445 449 446 450 export function startServer() {
+26 -12
update.sh
··· 1 1 #!/bin/bash 2 2 3 + set -e 4 + 3 5 echo "🔄 Tweets-2-Bsky Updater" 4 6 echo "=========================" 5 7 8 + CONFIG_FILE="config.json" 9 + CONFIG_BACKUP="" 10 + 11 + if [ -f "$CONFIG_FILE" ]; then 12 + CONFIG_BACKUP=$(mktemp) 13 + cp "$CONFIG_FILE" "$CONFIG_BACKUP" 14 + echo "🛡️ Backed up config.json to protect local settings." 15 + fi 16 + 17 + restore_config() { 18 + if [ -n "$CONFIG_BACKUP" ] && [ -f "$CONFIG_BACKUP" ]; then 19 + cp "$CONFIG_BACKUP" "$CONFIG_FILE" 20 + rm -f "$CONFIG_BACKUP" 21 + echo "🔐 Restored config.json." 22 + fi 23 + } 24 + 25 + trap restore_config EXIT 26 + 6 27 # Check if git is available 7 28 if ! command -v git &> /dev/null; then 8 29 echo "❌ Git is not installed. Please install git to update." ··· 24 45 echo "📦 Installing dependencies..." 25 46 npm install 26 47 27 - if [ $? -ne 0 ]; then 28 - echo "❌ npm install failed." 29 - exit 1 30 - fi 48 + echo "🔧 Verifying native modules..." 49 + npm run rebuild:native 31 50 32 - echo "🏗️ Building project..." 51 + echo "🏗️ Building server + web dashboard..." 33 52 npm run build 34 53 35 - if [ $? -ne 0 ]; then 36 - echo "❌ Build failed." 37 - exit 1 38 - fi 39 - 40 54 echo "✅ Update complete!" 41 55 42 56 # Determine PM2 process name (default to 'tweets-2-bsky' if not found) ··· 47 61 48 62 if command -v pm2 &> /dev/null; then 49 63 echo "🔄 Hard restarting PM2 process '$PROCESS_NAME' to fix environment paths..." 50 - pm2 delete $PROCESS_NAME 64 + pm2 delete $PROCESS_NAME || true 51 65 pm2 start dist/index.js --name $PROCESS_NAME 52 66 pm2 save 53 67 echo "✅ PM2 process restarted and saved." 54 68 else 55 69 echo "⚠️ PM2 not found. Please restart your application manually." 56 - fi 70 + fi
+26
vite.config.ts
··· 1 + import { fileURLToPath } from 'node:url'; 2 + import { dirname, resolve } from 'node:path'; 3 + import { defineConfig } from 'vite'; 4 + import react from '@vitejs/plugin-react'; 5 + 6 + const __filename = fileURLToPath(import.meta.url); 7 + const __dirname = dirname(__filename); 8 + 9 + export default defineConfig({ 10 + root: resolve(__dirname, 'web'), 11 + plugins: [react()], 12 + server: { 13 + port: 5173, 14 + host: '0.0.0.0', 15 + proxy: { 16 + '/api': { 17 + target: 'http://localhost:3000', 18 + changeOrigin: true, 19 + }, 20 + }, 21 + }, 22 + build: { 23 + outDir: resolve(__dirname, 'web/dist'), 24 + emptyOutDir: true, 25 + }, 26 + });
+13
web/index.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 + <title>Tweets-2-Bsky Dashboard</title> 7 + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> 8 + </head> 9 + <body> 10 + <div id="root"></div> 11 + <script type="module" src="/src/main.tsx"></script> 12 + </body> 13 + </html>
+10
web/public/favicon.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> 2 + <defs> 3 + <linearGradient id="bg" x1="0%" y1="0%" x2="0%" y2="100%"> 4 + <stop offset="0%" style="stop-color:#ffffff;stop-opacity:1" /> 5 + <stop offset="100%" style="stop-color:#0085ff;stop-opacity:1" /> 6 + </linearGradient> 7 + </defs> 8 + <rect width="512" height="512" rx="100" fill="url(#bg)" /> 9 + <path d="M405 156c-11.5 5.1-23.9 8.6-36.9 10.1 13.3-8 23.5-20.6 28.3-35.7-12.4 7.4-26.2 12.7-40.8 15.6-11.7-12.5-28.4-20.3-46.8-20.3-35.4 0-64.2 28.7-64.2 64.2 0 5 .6 9.9 1.7 14.6-53.3-2.7-100.6-28.2-132.2-67-5.5 9.5-8.7 20.5-8.7 32.3 0 22.3 11.3 41.9 28.5 53.4-10.5-.3-20.4-3.2-29.1-8v.8c0 31.1 22.1 57 51.5 62.9-5.4 1.5-11.1 2.3-16.9 2.3-4.1 0-8.2-.4-12.1-1.2 8.2 25.5 31.9 44.1 60 44.6-22 17.2-49.6 27.5-79.7 27.5-5.2 0-10.3-.3-15.3-.9 28.4 18.2 62.1 28.8 98.4 28.8 118 0 182.5-97.7 182.5-182.5 0-2.8-.1-5.5-.2-8.3 12.5-9 23.4-20.3 32-33.2z" fill="#0085ff"/> 10 + </svg>
+1501
web/src/App.tsx
··· 1 + import axios from 'axios'; 2 + import { 3 + AlertTriangle, 4 + ArrowUpRight, 5 + Bot, 6 + Clock3, 7 + Download, 8 + History, 9 + Loader2, 10 + LogOut, 11 + Moon, 12 + Play, 13 + Plus, 14 + Save, 15 + Settings2, 16 + Sun, 17 + SunMoon, 18 + Trash2, 19 + Upload, 20 + UserRound, 21 + } from 'lucide-react'; 22 + import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; 23 + import { Badge } from './components/ui/badge'; 24 + import { Button } from './components/ui/button'; 25 + import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './components/ui/card'; 26 + import { Input } from './components/ui/input'; 27 + import { Label } from './components/ui/label'; 28 + import { cn } from './lib/utils'; 29 + 30 + type ThemeMode = 'system' | 'light' | 'dark'; 31 + type AuthView = 'login' | 'register'; 32 + 33 + type AppState = 'idle' | 'checking' | 'backfilling' | 'pacing' | 'processing'; 34 + 35 + interface AccountMapping { 36 + id: string; 37 + twitterUsernames: string[]; 38 + bskyIdentifier: string; 39 + bskyPassword: string; 40 + bskyServiceUrl?: string; 41 + enabled: boolean; 42 + owner?: string; 43 + } 44 + 45 + interface TwitterConfig { 46 + authToken: string; 47 + ct0: string; 48 + backupAuthToken?: string; 49 + backupCt0?: string; 50 + } 51 + 52 + interface AIConfig { 53 + provider: 'gemini' | 'openai' | 'anthropic' | 'custom'; 54 + apiKey?: string; 55 + model?: string; 56 + baseUrl?: string; 57 + } 58 + 59 + interface ActivityLog { 60 + twitter_id: string; 61 + twitter_username: string; 62 + bsky_identifier: string; 63 + tweet_text?: string; 64 + bsky_uri?: string; 65 + status: 'migrated' | 'skipped' | 'failed'; 66 + created_at?: string; 67 + } 68 + 69 + interface PendingBackfill { 70 + id: string; 71 + limit?: number; 72 + queuedAt: number; 73 + sequence: number; 74 + requestId: string; 75 + position: number; 76 + } 77 + 78 + interface StatusState { 79 + state: AppState; 80 + currentAccount?: string; 81 + processedCount?: number; 82 + totalCount?: number; 83 + message?: string; 84 + backfillMappingId?: string; 85 + backfillRequestId?: string; 86 + lastUpdate: number; 87 + } 88 + 89 + interface StatusResponse { 90 + lastCheckTime: number; 91 + nextCheckTime: number; 92 + nextCheckMinutes: number; 93 + checkIntervalMinutes: number; 94 + pendingBackfills: PendingBackfill[]; 95 + currentStatus: StatusState; 96 + } 97 + 98 + interface AuthUser { 99 + email: string; 100 + isAdmin: boolean; 101 + } 102 + 103 + interface Notice { 104 + tone: 'success' | 'error' | 'info'; 105 + message: string; 106 + } 107 + 108 + interface MappingFormState { 109 + owner: string; 110 + twitterUsernames: string; 111 + bskyIdentifier: string; 112 + bskyPassword: string; 113 + bskyServiceUrl: string; 114 + } 115 + 116 + const defaultMappingForm = (): MappingFormState => ({ 117 + owner: '', 118 + twitterUsernames: '', 119 + bskyIdentifier: '', 120 + bskyPassword: '', 121 + bskyServiceUrl: 'https://bsky.social', 122 + }); 123 + 124 + const selectClassName = 125 + 'flex h-10 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2'; 126 + 127 + function getApiErrorMessage(error: unknown, fallback: string): string { 128 + if (axios.isAxiosError(error)) { 129 + const serverMessage = error.response?.data?.error; 130 + if (typeof serverMessage === 'string' && serverMessage.length > 0) { 131 + return serverMessage; 132 + } 133 + if (typeof error.message === 'string' && error.message.length > 0) { 134 + return error.message; 135 + } 136 + } 137 + return fallback; 138 + } 139 + 140 + function formatState(state: AppState): string { 141 + switch (state) { 142 + case 'checking': 143 + return 'Checking'; 144 + case 'backfilling': 145 + return 'Backfilling'; 146 + case 'pacing': 147 + return 'Pacing'; 148 + case 'processing': 149 + return 'Processing'; 150 + default: 151 + return 'Idle'; 152 + } 153 + } 154 + 155 + function getBskyPostUrl(activity: ActivityLog): string | null { 156 + if (!activity.bsky_uri || !activity.bsky_identifier) { 157 + return null; 158 + } 159 + 160 + const postId = activity.bsky_uri.split('/').filter(Boolean).pop(); 161 + if (!postId) { 162 + return null; 163 + } 164 + 165 + return `https://bsky.app/profile/${activity.bsky_identifier}/post/${postId}`; 166 + } 167 + 168 + function App() { 169 + const [token, setToken] = useState<string | null>(() => localStorage.getItem('token')); 170 + const [authView, setAuthView] = useState<AuthView>('login'); 171 + const [themeMode, setThemeMode] = useState<ThemeMode>(() => { 172 + const saved = localStorage.getItem('theme-mode'); 173 + if (saved === 'light' || saved === 'dark' || saved === 'system') { 174 + return saved; 175 + } 176 + return 'system'; 177 + }); 178 + const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light'); 179 + 180 + const [mappings, setMappings] = useState<AccountMapping[]>([]); 181 + const [twitterConfig, setTwitterConfig] = useState<TwitterConfig>({ authToken: '', ct0: '' }); 182 + const [aiConfig, setAiConfig] = useState<AIConfig>({ provider: 'gemini', apiKey: '', model: '', baseUrl: '' }); 183 + const [recentActivity, setRecentActivity] = useState<ActivityLog[]>([]); 184 + const [status, setStatus] = useState<StatusResponse | null>(null); 185 + const [countdown, setCountdown] = useState('--'); 186 + 187 + const [me, setMe] = useState<AuthUser | null>(null); 188 + const [editingMapping, setEditingMapping] = useState<AccountMapping | null>(null); 189 + const [newMapping, setNewMapping] = useState<MappingFormState>(defaultMappingForm); 190 + const [editForm, setEditForm] = useState<MappingFormState>(defaultMappingForm); 191 + const [notice, setNotice] = useState<Notice | null>(null); 192 + 193 + const [isBusy, setIsBusy] = useState(false); 194 + const [authError, setAuthError] = useState(''); 195 + 196 + const noticeTimerRef = useRef<number | null>(null); 197 + const importInputRef = useRef<HTMLInputElement>(null); 198 + 199 + const isAdmin = me?.isAdmin ?? false; 200 + const authHeaders = useMemo(() => (token ? { Authorization: `Bearer ${token}` } : undefined), [token]); 201 + 202 + const showNotice = useCallback((tone: Notice['tone'], message: string) => { 203 + setNotice({ tone, message }); 204 + if (noticeTimerRef.current) { 205 + window.clearTimeout(noticeTimerRef.current); 206 + } 207 + noticeTimerRef.current = window.setTimeout(() => { 208 + setNotice(null); 209 + }, 4200); 210 + }, []); 211 + 212 + const handleLogout = useCallback(() => { 213 + localStorage.removeItem('token'); 214 + setToken(null); 215 + setMe(null); 216 + setMappings([]); 217 + setStatus(null); 218 + setRecentActivity([]); 219 + setEditingMapping(null); 220 + setAuthView('login'); 221 + }, []); 222 + 223 + const handleAuthFailure = useCallback( 224 + (error: unknown, fallbackMessage: string) => { 225 + if (axios.isAxiosError(error) && (error.response?.status === 401 || error.response?.status === 403)) { 226 + handleLogout(); 227 + return; 228 + } 229 + showNotice('error', getApiErrorMessage(error, fallbackMessage)); 230 + }, 231 + [handleLogout, showNotice], 232 + ); 233 + 234 + const fetchStatus = useCallback(async () => { 235 + if (!authHeaders) { 236 + return; 237 + } 238 + 239 + try { 240 + const response = await axios.get<StatusResponse>('/api/status', { headers: authHeaders }); 241 + setStatus(response.data); 242 + } catch (error) { 243 + handleAuthFailure(error, 'Failed to fetch status.'); 244 + } 245 + }, [authHeaders, handleAuthFailure]); 246 + 247 + const fetchRecentActivity = useCallback(async () => { 248 + if (!authHeaders) { 249 + return; 250 + } 251 + 252 + try { 253 + const response = await axios.get<ActivityLog[]>('/api/recent-activity?limit=20', { headers: authHeaders }); 254 + setRecentActivity(response.data); 255 + } catch (error) { 256 + handleAuthFailure(error, 'Failed to fetch activity.'); 257 + } 258 + }, [authHeaders, handleAuthFailure]); 259 + 260 + const fetchData = useCallback(async () => { 261 + if (!authHeaders) { 262 + return; 263 + } 264 + 265 + try { 266 + const [meResponse, mappingsResponse] = await Promise.all([ 267 + axios.get<AuthUser>('/api/me', { headers: authHeaders }), 268 + axios.get<AccountMapping[]>('/api/mappings', { headers: authHeaders }), 269 + ]); 270 + 271 + const profile = meResponse.data; 272 + setMe(profile); 273 + setMappings(mappingsResponse.data); 274 + 275 + if (profile.isAdmin) { 276 + const [twitterResponse, aiResponse] = await Promise.all([ 277 + axios.get<TwitterConfig>('/api/twitter-config', { headers: authHeaders }), 278 + axios.get<AIConfig>('/api/ai-config', { headers: authHeaders }), 279 + ]); 280 + 281 + setTwitterConfig({ 282 + authToken: twitterResponse.data.authToken || '', 283 + ct0: twitterResponse.data.ct0 || '', 284 + backupAuthToken: twitterResponse.data.backupAuthToken || '', 285 + backupCt0: twitterResponse.data.backupCt0 || '', 286 + }); 287 + 288 + setAiConfig({ 289 + provider: aiResponse.data.provider || 'gemini', 290 + apiKey: aiResponse.data.apiKey || '', 291 + model: aiResponse.data.model || '', 292 + baseUrl: aiResponse.data.baseUrl || '', 293 + }); 294 + } 295 + 296 + await Promise.all([fetchStatus(), fetchRecentActivity()]); 297 + } catch (error) { 298 + handleAuthFailure(error, 'Failed to load dashboard data.'); 299 + } 300 + }, [authHeaders, fetchRecentActivity, fetchStatus, handleAuthFailure]); 301 + 302 + useEffect(() => { 303 + localStorage.setItem('theme-mode', themeMode); 304 + }, [themeMode]); 305 + 306 + useEffect(() => { 307 + const media = window.matchMedia('(prefers-color-scheme: dark)'); 308 + 309 + const applyTheme = () => { 310 + const next = themeMode === 'system' ? (media.matches ? 'dark' : 'light') : themeMode; 311 + setResolvedTheme(next); 312 + document.documentElement.classList.remove('light', 'dark'); 313 + document.documentElement.classList.add(next); 314 + }; 315 + 316 + applyTheme(); 317 + media.addEventListener('change', applyTheme); 318 + 319 + return () => { 320 + media.removeEventListener('change', applyTheme); 321 + }; 322 + }, [themeMode]); 323 + 324 + useEffect(() => { 325 + if (!token) { 326 + return; 327 + } 328 + 329 + void fetchData(); 330 + }, [token, fetchData]); 331 + 332 + useEffect(() => { 333 + if (!token) { 334 + return; 335 + } 336 + 337 + const statusInterval = window.setInterval(() => { 338 + void fetchStatus(); 339 + }, 2000); 340 + 341 + const activityInterval = window.setInterval(() => { 342 + void fetchRecentActivity(); 343 + }, 7000); 344 + 345 + return () => { 346 + window.clearInterval(statusInterval); 347 + window.clearInterval(activityInterval); 348 + }; 349 + }, [token, fetchRecentActivity, fetchStatus]); 350 + 351 + useEffect(() => { 352 + if (!status?.nextCheckTime) { 353 + setCountdown('--'); 354 + return; 355 + } 356 + 357 + const updateCountdown = () => { 358 + const ms = status.nextCheckTime - Date.now(); 359 + if (ms <= 0) { 360 + setCountdown('Checking...'); 361 + return; 362 + } 363 + 364 + const minutes = Math.floor(ms / 60000); 365 + const seconds = Math.floor((ms % 60000) / 1000); 366 + setCountdown(`${minutes}m ${String(seconds).padStart(2, '0')}s`); 367 + }; 368 + 369 + updateCountdown(); 370 + const timer = window.setInterval(updateCountdown, 1000); 371 + 372 + return () => { 373 + window.clearInterval(timer); 374 + }; 375 + }, [status?.nextCheckTime]); 376 + 377 + useEffect(() => { 378 + return () => { 379 + if (noticeTimerRef.current) { 380 + window.clearTimeout(noticeTimerRef.current); 381 + } 382 + }; 383 + }, []); 384 + 385 + const pendingBackfills = status?.pendingBackfills ?? []; 386 + const currentStatus = status?.currentStatus; 387 + const postedActivity = useMemo( 388 + () => 389 + recentActivity 390 + .filter((activity) => activity.status === 'migrated' && Boolean(getBskyPostUrl(activity))) 391 + .slice(0, 12), 392 + [recentActivity], 393 + ); 394 + 395 + const isBackfillQueued = useCallback( 396 + (mappingId: string) => pendingBackfills.some((entry) => entry.id === mappingId), 397 + [pendingBackfills], 398 + ); 399 + 400 + const getBackfillEntry = useCallback( 401 + (mappingId: string) => pendingBackfills.find((entry) => entry.id === mappingId), 402 + [pendingBackfills], 403 + ); 404 + 405 + const isBackfillActive = useCallback( 406 + (mappingId: string) => currentStatus?.state === 'backfilling' && currentStatus.backfillMappingId === mappingId, 407 + [currentStatus], 408 + ); 409 + 410 + const progressPercent = useMemo(() => { 411 + if (!currentStatus?.totalCount || currentStatus.totalCount <= 0) { 412 + return 0; 413 + } 414 + const processed = currentStatus.processedCount || 0; 415 + return Math.max(0, Math.min(100, Math.round((processed / currentStatus.totalCount) * 100))); 416 + }, [currentStatus]); 417 + 418 + const cycleThemeMode = () => { 419 + setThemeMode((prev) => { 420 + if (prev === 'system') return 'light'; 421 + if (prev === 'light') return 'dark'; 422 + return 'system'; 423 + }); 424 + }; 425 + 426 + const themeIcon = 427 + themeMode === 'system' ? <SunMoon className="h-4 w-4" /> : themeMode === 'light' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />; 428 + 429 + const themeLabel = 430 + themeMode === 'system' ? `Theme: system (${resolvedTheme})` : `Theme: ${themeMode}`; 431 + 432 + const handleLogin = async (event: React.FormEvent<HTMLFormElement>) => { 433 + event.preventDefault(); 434 + setAuthError(''); 435 + setIsBusy(true); 436 + 437 + const data = new FormData(event.currentTarget); 438 + const email = String(data.get('email') || '').trim(); 439 + const password = String(data.get('password') || ''); 440 + 441 + try { 442 + const response = await axios.post<{ token: string }>('/api/login', { email, password }); 443 + localStorage.setItem('token', response.data.token); 444 + setToken(response.data.token); 445 + showNotice('success', 'Logged in.'); 446 + } catch (error) { 447 + setAuthError(getApiErrorMessage(error, 'Invalid credentials.')); 448 + } finally { 449 + setIsBusy(false); 450 + } 451 + }; 452 + 453 + const handleRegister = async (event: React.FormEvent<HTMLFormElement>) => { 454 + event.preventDefault(); 455 + setAuthError(''); 456 + setIsBusy(true); 457 + 458 + const data = new FormData(event.currentTarget); 459 + const email = String(data.get('email') || '').trim(); 460 + const password = String(data.get('password') || ''); 461 + 462 + try { 463 + await axios.post('/api/register', { email, password }); 464 + setAuthView('login'); 465 + showNotice('success', 'Registration successful. Please log in.'); 466 + } catch (error) { 467 + setAuthError(getApiErrorMessage(error, 'Registration failed.')); 468 + } finally { 469 + setIsBusy(false); 470 + } 471 + }; 472 + 473 + const runNow = async () => { 474 + if (!authHeaders) { 475 + return; 476 + } 477 + 478 + try { 479 + await axios.post('/api/run-now', {}, { headers: authHeaders }); 480 + showNotice('info', 'Check triggered.'); 481 + await fetchStatus(); 482 + } catch (error) { 483 + handleAuthFailure(error, 'Failed to trigger a check.'); 484 + } 485 + }; 486 + 487 + const clearAllBackfills = async () => { 488 + if (!authHeaders) { 489 + return; 490 + } 491 + 492 + const confirmed = window.confirm('Stop all pending and active backfills?'); 493 + if (!confirmed) { 494 + return; 495 + } 496 + 497 + try { 498 + await axios.post('/api/backfill/clear-all', {}, { headers: authHeaders }); 499 + showNotice('success', 'Backfill queue cleared.'); 500 + await fetchStatus(); 501 + } catch (error) { 502 + handleAuthFailure(error, 'Failed to clear backfill queue.'); 503 + } 504 + }; 505 + 506 + const requestBackfill = async (mappingId: string, mode: 'normal' | 'reset') => { 507 + if (!authHeaders) { 508 + return; 509 + } 510 + 511 + const busy = pendingBackfills.length > 0 || currentStatus?.state === 'backfilling'; 512 + if (busy) { 513 + const proceed = window.confirm( 514 + 'Backfill is already queued or active. This request will replace the existing queue item for this account. Continue?', 515 + ); 516 + if (!proceed) { 517 + return; 518 + } 519 + } 520 + 521 + const limitInput = window.prompt(`How many tweets should be backfilled for this account?`, '15'); 522 + if (limitInput === null) { 523 + return; 524 + } 525 + 526 + const limit = Number.parseInt(limitInput, 10); 527 + const safeLimit = Number.isFinite(limit) && limit > 0 ? limit : 15; 528 + 529 + try { 530 + if (mode === 'reset') { 531 + await axios.delete(`/api/mappings/${mappingId}/cache`, { headers: authHeaders }); 532 + } 533 + 534 + await axios.post(`/api/backfill/${mappingId}`, { limit: safeLimit }, { headers: authHeaders }); 535 + showNotice('success', mode === 'reset' ? 'Cache reset and backfill queued.' : 'Backfill queued.'); 536 + await fetchStatus(); 537 + } catch (error) { 538 + handleAuthFailure(error, 'Failed to queue backfill.'); 539 + } 540 + }; 541 + 542 + const handleDeleteAllPosts = async (mappingId: string) => { 543 + if (!authHeaders) { 544 + return; 545 + } 546 + 547 + const firstConfirm = window.confirm( 548 + 'Danger: this deletes all posts on the mapped Bluesky account and clears local cache. Continue?', 549 + ); 550 + 551 + if (!firstConfirm) { 552 + return; 553 + } 554 + 555 + const finalConfirm = window.prompt('Type DELETE to confirm:'); 556 + if (finalConfirm !== 'DELETE') { 557 + return; 558 + } 559 + 560 + try { 561 + const response = await axios.post<{ message: string }>( 562 + `/api/mappings/${mappingId}/delete-all-posts`, 563 + {}, 564 + { headers: authHeaders }, 565 + ); 566 + showNotice('success', response.data.message); 567 + } catch (error) { 568 + handleAuthFailure(error, 'Failed to delete posts.'); 569 + } 570 + }; 571 + 572 + const handleDeleteMapping = async (mappingId: string) => { 573 + if (!authHeaders) { 574 + return; 575 + } 576 + 577 + const confirmed = window.confirm('Delete this mapping?'); 578 + if (!confirmed) { 579 + return; 580 + } 581 + 582 + try { 583 + await axios.delete(`/api/mappings/${mappingId}`, { headers: authHeaders }); 584 + setMappings((prev) => prev.filter((mapping) => mapping.id !== mappingId)); 585 + showNotice('success', 'Mapping deleted.'); 586 + await fetchData(); 587 + } catch (error) { 588 + handleAuthFailure(error, 'Failed to delete mapping.'); 589 + } 590 + }; 591 + 592 + const handleAddMapping = async (event: React.FormEvent<HTMLFormElement>) => { 593 + event.preventDefault(); 594 + if (!authHeaders) { 595 + return; 596 + } 597 + 598 + setIsBusy(true); 599 + 600 + try { 601 + await axios.post( 602 + '/api/mappings', 603 + { 604 + owner: newMapping.owner.trim(), 605 + twitterUsernames: newMapping.twitterUsernames, 606 + bskyIdentifier: newMapping.bskyIdentifier.trim(), 607 + bskyPassword: newMapping.bskyPassword, 608 + bskyServiceUrl: newMapping.bskyServiceUrl.trim(), 609 + }, 610 + { headers: authHeaders }, 611 + ); 612 + 613 + setNewMapping(defaultMappingForm()); 614 + showNotice('success', 'Account mapping added.'); 615 + await fetchData(); 616 + } catch (error) { 617 + handleAuthFailure(error, 'Failed to add account mapping.'); 618 + } finally { 619 + setIsBusy(false); 620 + } 621 + }; 622 + 623 + const startEditMapping = (mapping: AccountMapping) => { 624 + setEditingMapping(mapping); 625 + setEditForm({ 626 + owner: mapping.owner || '', 627 + twitterUsernames: mapping.twitterUsernames.join(', '), 628 + bskyIdentifier: mapping.bskyIdentifier, 629 + bskyPassword: '', 630 + bskyServiceUrl: mapping.bskyServiceUrl || 'https://bsky.social', 631 + }); 632 + }; 633 + 634 + const handleUpdateMapping = async (event: React.FormEvent<HTMLFormElement>) => { 635 + event.preventDefault(); 636 + if (!authHeaders || !editingMapping) { 637 + return; 638 + } 639 + 640 + setIsBusy(true); 641 + 642 + try { 643 + await axios.put( 644 + `/api/mappings/${editingMapping.id}`, 645 + { 646 + owner: editForm.owner.trim(), 647 + twitterUsernames: editForm.twitterUsernames, 648 + bskyIdentifier: editForm.bskyIdentifier.trim(), 649 + bskyPassword: editForm.bskyPassword, 650 + bskyServiceUrl: editForm.bskyServiceUrl.trim(), 651 + }, 652 + { headers: authHeaders }, 653 + ); 654 + 655 + setEditingMapping(null); 656 + setEditForm(defaultMappingForm()); 657 + showNotice('success', 'Mapping updated.'); 658 + await fetchData(); 659 + } catch (error) { 660 + handleAuthFailure(error, 'Failed to update mapping.'); 661 + } finally { 662 + setIsBusy(false); 663 + } 664 + }; 665 + 666 + const handleSaveTwitterConfig = async (event: React.FormEvent<HTMLFormElement>) => { 667 + event.preventDefault(); 668 + if (!authHeaders) { 669 + return; 670 + } 671 + 672 + setIsBusy(true); 673 + 674 + try { 675 + await axios.post( 676 + '/api/twitter-config', 677 + { 678 + authToken: twitterConfig.authToken, 679 + ct0: twitterConfig.ct0, 680 + backupAuthToken: twitterConfig.backupAuthToken, 681 + backupCt0: twitterConfig.backupCt0, 682 + }, 683 + { headers: authHeaders }, 684 + ); 685 + showNotice('success', 'Twitter credentials saved.'); 686 + await fetchData(); 687 + } catch (error) { 688 + handleAuthFailure(error, 'Failed to save Twitter credentials.'); 689 + } finally { 690 + setIsBusy(false); 691 + } 692 + }; 693 + 694 + const handleSaveAiConfig = async (event: React.FormEvent<HTMLFormElement>) => { 695 + event.preventDefault(); 696 + if (!authHeaders) { 697 + return; 698 + } 699 + 700 + setIsBusy(true); 701 + 702 + try { 703 + await axios.post( 704 + '/api/ai-config', 705 + { 706 + provider: aiConfig.provider, 707 + apiKey: aiConfig.apiKey, 708 + model: aiConfig.model, 709 + baseUrl: aiConfig.baseUrl, 710 + }, 711 + { headers: authHeaders }, 712 + ); 713 + showNotice('success', 'AI settings saved.'); 714 + await fetchData(); 715 + } catch (error) { 716 + handleAuthFailure(error, 'Failed to save AI settings.'); 717 + } finally { 718 + setIsBusy(false); 719 + } 720 + }; 721 + 722 + const handleExportConfig = async () => { 723 + if (!authHeaders) { 724 + return; 725 + } 726 + 727 + try { 728 + const response = await axios.get<Blob>('/api/config/export', { 729 + headers: authHeaders, 730 + responseType: 'blob', 731 + }); 732 + 733 + const blobUrl = window.URL.createObjectURL(new Blob([response.data])); 734 + const link = document.createElement('a'); 735 + link.href = blobUrl; 736 + link.download = `tweets-2-bsky-config-${new Date().toISOString().slice(0, 10)}.json`; 737 + document.body.appendChild(link); 738 + link.click(); 739 + link.remove(); 740 + window.URL.revokeObjectURL(blobUrl); 741 + showNotice('success', 'Configuration exported.'); 742 + } catch (error) { 743 + handleAuthFailure(error, 'Failed to export configuration.'); 744 + } 745 + }; 746 + 747 + const handleImportConfig = async (event: React.ChangeEvent<HTMLInputElement>) => { 748 + if (!authHeaders) { 749 + return; 750 + } 751 + 752 + const file = event.target.files?.[0]; 753 + if (!file) { 754 + return; 755 + } 756 + 757 + const confirmed = window.confirm( 758 + 'This will overwrite accounts/settings (except user logins). Continue with import?', 759 + ); 760 + 761 + if (!confirmed) { 762 + event.target.value = ''; 763 + return; 764 + } 765 + 766 + try { 767 + const text = await file.text(); 768 + const json = JSON.parse(text); 769 + 770 + await axios.post('/api/config/import', json, { headers: authHeaders }); 771 + showNotice('success', 'Configuration imported.'); 772 + await fetchData(); 773 + } catch (error) { 774 + handleAuthFailure(error, 'Failed to import configuration.'); 775 + } finally { 776 + event.target.value = ''; 777 + } 778 + }; 779 + 780 + if (!token) { 781 + return ( 782 + <main className="flex min-h-screen items-center justify-center p-4"> 783 + <Card className="w-full max-w-md animate-slide-up border-border/80 bg-card/95"> 784 + <CardHeader className="space-y-1"> 785 + <CardTitle className="text-2xl">Tweets-2-Bsky</CardTitle> 786 + <CardDescription> 787 + {authView === 'login' 788 + ? 'Sign in to manage mappings, status, and account settings.' 789 + : 'Create your first dashboard account.'} 790 + </CardDescription> 791 + </CardHeader> 792 + <CardContent> 793 + {authError ? ( 794 + <div className="mb-4 rounded-md border border-red-500/40 bg-red-500/10 px-3 py-2 text-sm text-red-500 dark:text-red-300"> 795 + {authError} 796 + </div> 797 + ) : null} 798 + 799 + <form className="space-y-4" onSubmit={authView === 'login' ? handleLogin : handleRegister}> 800 + <div className="space-y-2"> 801 + <Label htmlFor="email">Email</Label> 802 + <Input id="email" name="email" type="email" autoComplete="email" required /> 803 + </div> 804 + 805 + <div className="space-y-2"> 806 + <Label htmlFor="password">Password</Label> 807 + <Input id="password" name="password" type="password" autoComplete="current-password" required /> 808 + </div> 809 + 810 + <Button className="w-full" type="submit" disabled={isBusy}> 811 + {isBusy ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null} 812 + {authView === 'login' ? 'Sign in' : 'Create account'} 813 + </Button> 814 + </form> 815 + 816 + <Button 817 + className="mt-4 w-full" 818 + variant="ghost" 819 + onClick={() => { 820 + setAuthError(''); 821 + setAuthView(authView === 'login' ? 'register' : 'login'); 822 + }} 823 + type="button" 824 + > 825 + {authView === 'login' ? 'Need an account? Register' : 'Have an account? Sign in'} 826 + </Button> 827 + </CardContent> 828 + </Card> 829 + </main> 830 + ); 831 + } 832 + 833 + return ( 834 + <main className="mx-auto w-full max-w-7xl px-4 py-6 sm:px-6 lg:px-8"> 835 + <div className="mb-6 animate-slide-up"> 836 + <Card className="border-border/80 bg-card/90"> 837 + <CardContent className="flex flex-wrap items-center justify-between gap-4 p-4 sm:p-5"> 838 + <div className="space-y-1"> 839 + <p className="text-xs font-medium uppercase tracking-[0.2em] text-muted-foreground">Dashboard</p> 840 + <h1 className="text-xl font-semibold sm:text-2xl">Tweets-2-Bsky Control Panel</h1> 841 + <p className="flex items-center gap-2 text-sm text-muted-foreground"> 842 + <Clock3 className="h-4 w-4" /> 843 + Next run in <span className="font-mono text-foreground">{countdown}</span> 844 + </p> 845 + </div> 846 + 847 + <div className="flex flex-wrap items-center gap-2"> 848 + <Button variant="outline" size="sm" onClick={cycleThemeMode} title={themeLabel}> 849 + {themeIcon} 850 + <span className="ml-2 hidden sm:inline">{themeLabel}</span> 851 + </Button> 852 + <Button size="sm" onClick={runNow}> 853 + <Play className="mr-2 h-4 w-4" /> 854 + Run now 855 + </Button> 856 + {isAdmin && pendingBackfills.length > 0 ? ( 857 + <Button size="sm" variant="destructive" onClick={clearAllBackfills}> 858 + <Trash2 className="mr-2 h-4 w-4" /> 859 + Clear queue 860 + </Button> 861 + ) : null} 862 + <Button size="sm" variant="ghost" onClick={handleLogout}> 863 + <LogOut className="mr-2 h-4 w-4" /> 864 + Logout 865 + </Button> 866 + </div> 867 + </CardContent> 868 + </Card> 869 + </div> 870 + 871 + {notice ? ( 872 + <div 873 + className={cn( 874 + 'mb-5 animate-fade-in rounded-md border px-4 py-2 text-sm', 875 + notice.tone === 'success' && 876 + 'border-emerald-500/40 bg-emerald-500/10 text-emerald-700 dark:border-emerald-500/30 dark:text-emerald-300', 877 + notice.tone === 'error' && 878 + 'border-red-500/40 bg-red-500/10 text-red-700 dark:border-red-500/30 dark:text-red-300', 879 + notice.tone === 'info' && 880 + 'border-border bg-muted text-muted-foreground', 881 + )} 882 + > 883 + {notice.message} 884 + </div> 885 + ) : null} 886 + 887 + {currentStatus && currentStatus.state !== 'idle' ? ( 888 + <Card className="mb-6 animate-fade-in border-border/80"> 889 + <div className="h-1 overflow-hidden rounded-t-xl bg-muted"> 890 + <div 891 + className={cn( 892 + 'h-full transition-all duration-300', 893 + currentStatus.state === 'backfilling' ? 'bg-amber-500' : 'bg-emerald-500', 894 + )} 895 + style={{ width: `${progressPercent || 100}%` }} 896 + /> 897 + </div> 898 + <CardContent className="flex flex-wrap items-center justify-between gap-3 p-4"> 899 + <div className="space-y-1"> 900 + <p className="text-sm font-semibold">{formatState(currentStatus.state)} in progress</p> 901 + <p className="text-sm text-muted-foreground"> 902 + {currentStatus.currentAccount ? `@${currentStatus.currentAccount} • ` : ''} 903 + {currentStatus.message || 'Working through account queue.'} 904 + </p> 905 + </div> 906 + <div className="text-right"> 907 + <p className="text-lg font-semibold">{progressPercent || 0}%</p> 908 + <p className="text-xs text-muted-foreground"> 909 + {(currentStatus.processedCount || 0).toLocaleString()} / {(currentStatus.totalCount || 0).toLocaleString()} 910 + </p> 911 + </div> 912 + </CardContent> 913 + </Card> 914 + ) : null} 915 + 916 + <div className="panel-grid"> 917 + <section className="space-y-6"> 918 + <Card className="animate-slide-up"> 919 + <CardHeader className="pb-3"> 920 + <div className="flex items-center justify-between"> 921 + <div className="space-y-1"> 922 + <CardTitle>Active Accounts</CardTitle> 923 + <CardDescription>Manage source-to-target mappings and run account actions.</CardDescription> 924 + </div> 925 + <Badge variant="outline">{mappings.length} configured</Badge> 926 + </div> 927 + </CardHeader> 928 + <CardContent className="pt-0"> 929 + {mappings.length === 0 ? ( 930 + <div className="rounded-lg border border-dashed border-border/70 p-6 text-center text-sm text-muted-foreground"> 931 + No mappings yet. Add one from the settings panel. 932 + </div> 933 + ) : ( 934 + <div className="overflow-x-auto"> 935 + <table className="min-w-full text-left text-sm"> 936 + <thead className="border-b border-border text-xs uppercase tracking-wide text-muted-foreground"> 937 + <tr> 938 + <th className="px-2 py-3">Owner</th> 939 + <th className="px-2 py-3">Twitter Sources</th> 940 + <th className="px-2 py-3">Bluesky Target</th> 941 + <th className="px-2 py-3">Status</th> 942 + <th className="px-2 py-3 text-right">Actions</th> 943 + </tr> 944 + </thead> 945 + <tbody> 946 + {mappings.map((mapping) => { 947 + const queued = isBackfillQueued(mapping.id); 948 + const active = isBackfillActive(mapping.id); 949 + const queuePosition = getBackfillEntry(mapping.id)?.position; 950 + 951 + return ( 952 + <tr key={mapping.id} className="border-b border-border/60 last:border-0"> 953 + <td className="px-2 py-3 align-top"> 954 + <div className="flex items-center gap-2 font-medium"> 955 + <UserRound className="h-4 w-4 text-muted-foreground" /> 956 + {mapping.owner || 'System'} 957 + </div> 958 + </td> 959 + <td className="px-2 py-3 align-top"> 960 + <div className="flex flex-wrap gap-2"> 961 + {mapping.twitterUsernames.map((username) => ( 962 + <Badge key={username} variant="secondary"> 963 + @{username} 964 + </Badge> 965 + ))} 966 + </div> 967 + </td> 968 + <td className="px-2 py-3 align-top"> 969 + <span className="font-mono text-xs sm:text-sm">{mapping.bskyIdentifier}</span> 970 + </td> 971 + <td className="px-2 py-3 align-top"> 972 + {active ? ( 973 + <Badge variant="warning">Backfilling</Badge> 974 + ) : queued ? ( 975 + <Badge variant="warning">Queued {queuePosition ? `#${queuePosition}` : ''}</Badge> 976 + ) : ( 977 + <Badge variant="success">Active</Badge> 978 + )} 979 + </td> 980 + <td className="px-2 py-3 align-top"> 981 + <div className="flex flex-wrap justify-end gap-1"> 982 + {isAdmin ? ( 983 + <> 984 + <Button variant="outline" size="sm" onClick={() => startEditMapping(mapping)}> 985 + Edit 986 + </Button> 987 + <Button 988 + variant="outline" 989 + size="sm" 990 + onClick={() => { 991 + void requestBackfill(mapping.id, 'normal'); 992 + }} 993 + > 994 + Backfill 995 + </Button> 996 + <Button 997 + variant="subtle" 998 + size="sm" 999 + onClick={() => { 1000 + void requestBackfill(mapping.id, 'reset'); 1001 + }} 1002 + > 1003 + Reset + Backfill 1004 + </Button> 1005 + <Button 1006 + variant="destructive" 1007 + size="sm" 1008 + onClick={() => { 1009 + void handleDeleteAllPosts(mapping.id); 1010 + }} 1011 + > 1012 + Delete Posts 1013 + </Button> 1014 + </> 1015 + ) : null} 1016 + <Button 1017 + variant="ghost" 1018 + size="sm" 1019 + onClick={() => { 1020 + void handleDeleteMapping(mapping.id); 1021 + }} 1022 + > 1023 + <Trash2 className="mr-1 h-4 w-4" /> 1024 + Remove 1025 + </Button> 1026 + </div> 1027 + </td> 1028 + </tr> 1029 + ); 1030 + })} 1031 + </tbody> 1032 + </table> 1033 + </div> 1034 + )} 1035 + </CardContent> 1036 + </Card> 1037 + 1038 + <Card className="animate-slide-up"> 1039 + <CardHeader className="pb-3"> 1040 + <CardTitle>Already Posted</CardTitle> 1041 + <CardDescription>Native-styled feed of successfully posted Bluesky entries.</CardDescription> 1042 + </CardHeader> 1043 + <CardContent className="pt-0"> 1044 + {postedActivity.length === 0 ? ( 1045 + <div className="rounded-lg border border-dashed border-border/70 p-6 text-center text-sm text-muted-foreground"> 1046 + No posted entries yet. 1047 + </div> 1048 + ) : ( 1049 + <div className="grid gap-3 md:grid-cols-2"> 1050 + {postedActivity.map((activity, index) => { 1051 + const postUrl = getBskyPostUrl(activity); 1052 + return ( 1053 + <article 1054 + key={`${activity.twitter_id}-${activity.created_at || index}-posted`} 1055 + className="rounded-xl border border-border/70 bg-background/80 p-4 shadow-sm" 1056 + > 1057 + <div className="mb-3 flex items-start justify-between gap-3"> 1058 + <div> 1059 + <p className="text-sm font-semibold">@{activity.bsky_identifier}</p> 1060 + <p className="text-xs text-muted-foreground">from @{activity.twitter_username}</p> 1061 + </div> 1062 + <Badge variant="success">Posted</Badge> 1063 + </div> 1064 + <p className="mb-3 whitespace-pre-wrap break-words text-sm leading-relaxed text-foreground"> 1065 + {activity.tweet_text || `(No cached text) Tweet ID ${activity.twitter_id}`} 1066 + </p> 1067 + <div className="flex items-center justify-between gap-3 text-xs text-muted-foreground"> 1068 + <span>{activity.created_at ? new Date(activity.created_at).toLocaleString() : 'Unknown time'}</span> 1069 + {postUrl ? ( 1070 + <a 1071 + className="inline-flex items-center text-foreground underline-offset-4 hover:underline" 1072 + href={postUrl} 1073 + target="_blank" 1074 + rel="noreferrer" 1075 + > 1076 + Open 1077 + <ArrowUpRight className="ml-1 h-3 w-3" /> 1078 + </a> 1079 + ) : ( 1080 + <span>Missing URI</span> 1081 + )} 1082 + </div> 1083 + </article> 1084 + ); 1085 + })} 1086 + </div> 1087 + )} 1088 + </CardContent> 1089 + </Card> 1090 + 1091 + <Card className="animate-slide-up"> 1092 + <CardHeader className="pb-3"> 1093 + <CardTitle className="flex items-center gap-2"> 1094 + <History className="h-4 w-4" /> 1095 + Recent Activity 1096 + </CardTitle> 1097 + <CardDescription>Latest migration outcomes from the processing database.</CardDescription> 1098 + </CardHeader> 1099 + <CardContent className="pt-0"> 1100 + <div className="overflow-x-auto"> 1101 + <table className="min-w-full text-left text-sm"> 1102 + <thead className="border-b border-border text-xs uppercase tracking-wide text-muted-foreground"> 1103 + <tr> 1104 + <th className="px-2 py-3">Time</th> 1105 + <th className="px-2 py-3">Twitter User</th> 1106 + <th className="px-2 py-3">Status</th> 1107 + <th className="px-2 py-3">Details</th> 1108 + <th className="px-2 py-3 text-right">Link</th> 1109 + </tr> 1110 + </thead> 1111 + <tbody> 1112 + {recentActivity.map((activity, index) => { 1113 + const href = getBskyPostUrl(activity); 1114 + 1115 + return ( 1116 + <tr 1117 + key={`${activity.twitter_id}-${activity.created_at || index}`} 1118 + className="border-b border-border/60 last:border-0" 1119 + > 1120 + <td className="px-2 py-3 align-top text-xs text-muted-foreground"> 1121 + {activity.created_at ? new Date(activity.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : '--'} 1122 + </td> 1123 + <td className="px-2 py-3 align-top font-medium">@{activity.twitter_username}</td> 1124 + <td className="px-2 py-3 align-top"> 1125 + {activity.status === 'migrated' ? ( 1126 + <Badge variant="success">Migrated</Badge> 1127 + ) : activity.status === 'skipped' ? ( 1128 + <Badge variant="outline">Skipped</Badge> 1129 + ) : ( 1130 + <Badge variant="danger">Failed</Badge> 1131 + )} 1132 + </td> 1133 + <td className="px-2 py-3 align-top text-xs text-muted-foreground"> 1134 + <div className="max-w-[340px] truncate">{activity.tweet_text || `Tweet ID: ${activity.twitter_id}`}</div> 1135 + </td> 1136 + <td className="px-2 py-3 align-top text-right"> 1137 + {href ? ( 1138 + <a className="inline-flex items-center text-xs text-foreground underline-offset-4 hover:underline" href={href} target="_blank" rel="noreferrer"> 1139 + Open 1140 + <ArrowUpRight className="ml-1 h-3 w-3" /> 1141 + </a> 1142 + ) : ( 1143 + <span className="text-xs text-muted-foreground">--</span> 1144 + )} 1145 + </td> 1146 + </tr> 1147 + ); 1148 + })} 1149 + {recentActivity.length === 0 ? ( 1150 + <tr> 1151 + <td className="px-2 py-6 text-center text-sm text-muted-foreground" colSpan={5}> 1152 + No activity yet. 1153 + </td> 1154 + </tr> 1155 + ) : null} 1156 + </tbody> 1157 + </table> 1158 + </div> 1159 + </CardContent> 1160 + </Card> 1161 + </section> 1162 + 1163 + {isAdmin ? ( 1164 + <aside className="space-y-6"> 1165 + <Card className="animate-slide-up"> 1166 + <CardHeader> 1167 + <CardTitle className="flex items-center gap-2"> 1168 + <Settings2 className="h-4 w-4" /> 1169 + Admin Settings 1170 + </CardTitle> 1171 + <CardDescription>Credentials, provider setup, and account onboarding.</CardDescription> 1172 + </CardHeader> 1173 + <CardContent className="space-y-8"> 1174 + <form className="space-y-3" onSubmit={handleSaveTwitterConfig}> 1175 + <div className="flex items-center justify-between"> 1176 + <h3 className="text-sm font-semibold">Twitter Credentials</h3> 1177 + <Badge variant={twitterConfig.authToken && twitterConfig.ct0 ? 'success' : 'outline'}> 1178 + {twitterConfig.authToken && twitterConfig.ct0 ? 'Configured' : 'Missing'} 1179 + </Badge> 1180 + </div> 1181 + <div className="space-y-2"> 1182 + <Label htmlFor="authToken">Primary Auth Token</Label> 1183 + <Input 1184 + id="authToken" 1185 + value={twitterConfig.authToken} 1186 + onChange={(event) => { 1187 + setTwitterConfig((prev) => ({ ...prev, authToken: event.target.value })); 1188 + }} 1189 + required 1190 + /> 1191 + </div> 1192 + <div className="space-y-2"> 1193 + <Label htmlFor="ct0">Primary CT0</Label> 1194 + <Input 1195 + id="ct0" 1196 + value={twitterConfig.ct0} 1197 + onChange={(event) => { 1198 + setTwitterConfig((prev) => ({ ...prev, ct0: event.target.value })); 1199 + }} 1200 + required 1201 + /> 1202 + </div> 1203 + 1204 + <div className="grid gap-3 sm:grid-cols-2"> 1205 + <div className="space-y-2"> 1206 + <Label htmlFor="backupAuthToken">Backup Auth Token</Label> 1207 + <Input 1208 + id="backupAuthToken" 1209 + value={twitterConfig.backupAuthToken || ''} 1210 + onChange={(event) => { 1211 + setTwitterConfig((prev) => ({ ...prev, backupAuthToken: event.target.value })); 1212 + }} 1213 + /> 1214 + </div> 1215 + <div className="space-y-2"> 1216 + <Label htmlFor="backupCt0">Backup CT0</Label> 1217 + <Input 1218 + id="backupCt0" 1219 + value={twitterConfig.backupCt0 || ''} 1220 + onChange={(event) => { 1221 + setTwitterConfig((prev) => ({ ...prev, backupCt0: event.target.value })); 1222 + }} 1223 + /> 1224 + </div> 1225 + </div> 1226 + 1227 + <Button className="w-full" size="sm" type="submit" disabled={isBusy}> 1228 + <Save className="mr-2 h-4 w-4" /> 1229 + Save Twitter Credentials 1230 + </Button> 1231 + </form> 1232 + 1233 + <form className="space-y-3 border-t border-border pt-6" onSubmit={handleSaveAiConfig}> 1234 + <div className="flex items-center justify-between"> 1235 + <h3 className="text-sm font-semibold">AI Settings</h3> 1236 + <Badge variant={aiConfig.apiKey ? 'success' : 'outline'}>{aiConfig.apiKey ? 'Configured' : 'Optional'}</Badge> 1237 + </div> 1238 + <div className="space-y-2"> 1239 + <Label htmlFor="provider">Provider</Label> 1240 + <select 1241 + className={selectClassName} 1242 + id="provider" 1243 + value={aiConfig.provider} 1244 + onChange={(event) => { 1245 + setAiConfig((prev) => ({ ...prev, provider: event.target.value as AIConfig['provider'] })); 1246 + }} 1247 + > 1248 + <option value="gemini">Google Gemini</option> 1249 + <option value="openai">OpenAI / OpenRouter</option> 1250 + <option value="anthropic">Anthropic</option> 1251 + <option value="custom">Custom</option> 1252 + </select> 1253 + </div> 1254 + <div className="space-y-2"> 1255 + <Label htmlFor="apiKey">API Key</Label> 1256 + <Input 1257 + id="apiKey" 1258 + type="password" 1259 + value={aiConfig.apiKey || ''} 1260 + onChange={(event) => { 1261 + setAiConfig((prev) => ({ ...prev, apiKey: event.target.value })); 1262 + }} 1263 + /> 1264 + </div> 1265 + {aiConfig.provider !== 'gemini' ? ( 1266 + <> 1267 + <div className="space-y-2"> 1268 + <Label htmlFor="model">Model ID</Label> 1269 + <Input 1270 + id="model" 1271 + value={aiConfig.model || ''} 1272 + onChange={(event) => { 1273 + setAiConfig((prev) => ({ ...prev, model: event.target.value })); 1274 + }} 1275 + placeholder="gpt-4o" 1276 + /> 1277 + </div> 1278 + <div className="space-y-2"> 1279 + <Label htmlFor="baseUrl">Base URL</Label> 1280 + <Input 1281 + id="baseUrl" 1282 + value={aiConfig.baseUrl || ''} 1283 + onChange={(event) => { 1284 + setAiConfig((prev) => ({ ...prev, baseUrl: event.target.value })); 1285 + }} 1286 + placeholder="https://api.example.com/v1" 1287 + /> 1288 + </div> 1289 + </> 1290 + ) : null} 1291 + 1292 + <Button className="w-full" size="sm" type="submit" disabled={isBusy}> 1293 + <Bot className="mr-2 h-4 w-4" /> 1294 + Save AI Settings 1295 + </Button> 1296 + </form> 1297 + 1298 + <form className="space-y-3 border-t border-border pt-6" onSubmit={handleAddMapping}> 1299 + <h3 className="text-sm font-semibold">Add Account Mapping</h3> 1300 + <div className="space-y-2"> 1301 + <Label htmlFor="owner">Owner</Label> 1302 + <Input 1303 + id="owner" 1304 + value={newMapping.owner} 1305 + onChange={(event) => { 1306 + setNewMapping((prev) => ({ ...prev, owner: event.target.value })); 1307 + }} 1308 + required 1309 + /> 1310 + </div> 1311 + <div className="space-y-2"> 1312 + <Label htmlFor="twitterUsernames">Twitter Usernames (comma separated)</Label> 1313 + <Input 1314 + id="twitterUsernames" 1315 + value={newMapping.twitterUsernames} 1316 + onChange={(event) => { 1317 + setNewMapping((prev) => ({ ...prev, twitterUsernames: event.target.value })); 1318 + }} 1319 + required 1320 + /> 1321 + </div> 1322 + <div className="space-y-2"> 1323 + <Label htmlFor="bskyIdentifier">Bluesky Identifier</Label> 1324 + <Input 1325 + id="bskyIdentifier" 1326 + value={newMapping.bskyIdentifier} 1327 + onChange={(event) => { 1328 + setNewMapping((prev) => ({ ...prev, bskyIdentifier: event.target.value })); 1329 + }} 1330 + required 1331 + /> 1332 + </div> 1333 + <div className="space-y-2"> 1334 + <Label htmlFor="bskyPassword">Bluesky App Password</Label> 1335 + <Input 1336 + id="bskyPassword" 1337 + type="password" 1338 + value={newMapping.bskyPassword} 1339 + onChange={(event) => { 1340 + setNewMapping((prev) => ({ ...prev, bskyPassword: event.target.value })); 1341 + }} 1342 + required 1343 + /> 1344 + </div> 1345 + <div className="space-y-2"> 1346 + <Label htmlFor="bskyServiceUrl">Bluesky Service URL</Label> 1347 + <Input 1348 + id="bskyServiceUrl" 1349 + value={newMapping.bskyServiceUrl} 1350 + onChange={(event) => { 1351 + setNewMapping((prev) => ({ ...prev, bskyServiceUrl: event.target.value })); 1352 + }} 1353 + placeholder="https://bsky.social" 1354 + /> 1355 + </div> 1356 + 1357 + <Button className="w-full" size="sm" type="submit" disabled={isBusy}> 1358 + <Plus className="mr-2 h-4 w-4" /> 1359 + Add Mapping 1360 + </Button> 1361 + </form> 1362 + </CardContent> 1363 + </Card> 1364 + 1365 + <Card className="animate-slide-up"> 1366 + <CardHeader> 1367 + <CardTitle>Data Management</CardTitle> 1368 + <CardDescription>Export/import account and provider config without login credentials.</CardDescription> 1369 + </CardHeader> 1370 + <CardContent className="space-y-3"> 1371 + <Button className="w-full" variant="outline" onClick={handleExportConfig}> 1372 + <Download className="mr-2 h-4 w-4" /> 1373 + Export configuration 1374 + </Button> 1375 + <input 1376 + ref={importInputRef} 1377 + className="hidden" 1378 + type="file" 1379 + accept="application/json,.json" 1380 + onChange={(event) => { 1381 + void handleImportConfig(event); 1382 + }} 1383 + /> 1384 + <Button 1385 + className="w-full" 1386 + variant="outline" 1387 + onClick={() => { 1388 + importInputRef.current?.click(); 1389 + }} 1390 + > 1391 + <Upload className="mr-2 h-4 w-4" /> 1392 + Import configuration 1393 + </Button> 1394 + <p className="text-xs text-muted-foreground"> 1395 + Imports preserve dashboard users and passwords while replacing mappings, provider keys, and scheduler settings. 1396 + </p> 1397 + </CardContent> 1398 + </Card> 1399 + </aside> 1400 + ) : null} 1401 + </div> 1402 + 1403 + {editingMapping ? ( 1404 + <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4 backdrop-blur-sm"> 1405 + <Card className="w-full max-w-xl animate-slide-up border-border/90 bg-card"> 1406 + <CardHeader> 1407 + <CardTitle>Edit Mapping</CardTitle> 1408 + <CardDescription>Update ownership, handles, and target credentials.</CardDescription> 1409 + </CardHeader> 1410 + <CardContent> 1411 + <form className="space-y-3" onSubmit={handleUpdateMapping}> 1412 + <div className="space-y-2"> 1413 + <Label htmlFor="edit-owner">Owner</Label> 1414 + <Input 1415 + id="edit-owner" 1416 + value={editForm.owner} 1417 + onChange={(event) => { 1418 + setEditForm((prev) => ({ ...prev, owner: event.target.value })); 1419 + }} 1420 + required 1421 + /> 1422 + </div> 1423 + <div className="space-y-2"> 1424 + <Label htmlFor="edit-twitterUsernames">Twitter Usernames</Label> 1425 + <Input 1426 + id="edit-twitterUsernames" 1427 + value={editForm.twitterUsernames} 1428 + onChange={(event) => { 1429 + setEditForm((prev) => ({ ...prev, twitterUsernames: event.target.value })); 1430 + }} 1431 + required 1432 + /> 1433 + </div> 1434 + <div className="space-y-2"> 1435 + <Label htmlFor="edit-bskyIdentifier">Bluesky Identifier</Label> 1436 + <Input 1437 + id="edit-bskyIdentifier" 1438 + value={editForm.bskyIdentifier} 1439 + onChange={(event) => { 1440 + setEditForm((prev) => ({ ...prev, bskyIdentifier: event.target.value })); 1441 + }} 1442 + required 1443 + /> 1444 + </div> 1445 + <div className="space-y-2"> 1446 + <Label htmlFor="edit-bskyPassword">New App Password (optional)</Label> 1447 + <Input 1448 + id="edit-bskyPassword" 1449 + type="password" 1450 + value={editForm.bskyPassword} 1451 + onChange={(event) => { 1452 + setEditForm((prev) => ({ ...prev, bskyPassword: event.target.value })); 1453 + }} 1454 + placeholder="Leave blank to keep existing" 1455 + /> 1456 + </div> 1457 + <div className="space-y-2"> 1458 + <Label htmlFor="edit-bskyServiceUrl">Service URL</Label> 1459 + <Input 1460 + id="edit-bskyServiceUrl" 1461 + value={editForm.bskyServiceUrl} 1462 + onChange={(event) => { 1463 + setEditForm((prev) => ({ ...prev, bskyServiceUrl: event.target.value })); 1464 + }} 1465 + /> 1466 + </div> 1467 + 1468 + <div className="flex flex-wrap justify-end gap-2 pt-2"> 1469 + <Button 1470 + variant="ghost" 1471 + type="button" 1472 + onClick={() => { 1473 + setEditingMapping(null); 1474 + }} 1475 + > 1476 + Cancel 1477 + </Button> 1478 + <Button type="submit" disabled={isBusy}> 1479 + {isBusy ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null} 1480 + Save changes 1481 + </Button> 1482 + </div> 1483 + </form> 1484 + </CardContent> 1485 + </Card> 1486 + </div> 1487 + ) : null} 1488 + 1489 + {!isAdmin ? ( 1490 + <div className="mt-6 rounded-lg border border-amber-500/30 bg-amber-500/10 p-3 text-sm text-amber-700 dark:text-amber-300"> 1491 + <p className="flex items-center gap-2"> 1492 + <AlertTriangle className="h-4 w-4" /> 1493 + Admin-only settings are hidden for this account. 1494 + </p> 1495 + </div> 1496 + ) : null} 1497 + </main> 1498 + ); 1499 + } 1500 + 1501 + export default App;
+27
web/src/components/ui/badge.tsx
··· 1 + import * as React from 'react'; 2 + import { cva, type VariantProps } from 'class-variance-authority'; 3 + import { cn } from '../../lib/utils'; 4 + 5 + const badgeVariants = cva('inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold', { 6 + variants: { 7 + variant: { 8 + default: 'border-transparent bg-foreground text-background', 9 + secondary: 'border-transparent bg-muted text-foreground', 10 + outline: 'border-border text-muted-foreground', 11 + success: 'border-emerald-500/30 bg-emerald-500/10 text-emerald-600 dark:text-emerald-300', 12 + warning: 'border-amber-500/30 bg-amber-500/10 text-amber-600 dark:text-amber-300', 13 + danger: 'border-red-500/30 bg-red-500/10 text-red-600 dark:text-red-300', 14 + }, 15 + }, 16 + defaultVariants: { 17 + variant: 'default', 18 + }, 19 + }); 20 + 21 + export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {} 22 + 23 + function Badge({ className, variant, ...props }: BadgeProps) { 24 + return <div className={cn(badgeVariants({ variant }), className)} {...props} />; 25 + } 26 + 27 + export { Badge, badgeVariants };
+41
web/src/components/ui/button.tsx
··· 1 + import * as React from 'react'; 2 + import { cva, type VariantProps } from 'class-variance-authority'; 3 + import { cn } from '../../lib/utils'; 4 + 5 + const buttonVariants = cva( 6 + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98]', 7 + { 8 + variants: { 9 + variant: { 10 + default: 'bg-foreground text-background shadow hover:opacity-90', 11 + outline: 'border border-border bg-background text-foreground hover:bg-muted', 12 + ghost: 'text-muted-foreground hover:bg-muted hover:text-foreground', 13 + destructive: 'bg-red-600 text-white hover:bg-red-700', 14 + subtle: 'bg-muted text-foreground hover:bg-muted/80', 15 + }, 16 + size: { 17 + default: 'h-10 px-4 py-2', 18 + sm: 'h-9 rounded-md px-3', 19 + lg: 'h-11 rounded-md px-8', 20 + icon: 'h-9 w-9', 21 + }, 22 + }, 23 + defaultVariants: { 24 + variant: 'default', 25 + size: 'default', 26 + }, 27 + }, 28 + ); 29 + 30 + export interface ButtonProps 31 + extends React.ButtonHTMLAttributes<HTMLButtonElement>, 32 + VariantProps<typeof buttonVariants> {} 33 + 34 + const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( 35 + ({ className, variant, size, ...props }, ref) => { 36 + return <button className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />; 37 + }, 38 + ); 39 + Button.displayName = 'Button'; 40 + 41 + export { Button, buttonVariants };
+38
web/src/components/ui/card.tsx
··· 1 + import * as React from 'react'; 2 + import { cn } from '../../lib/utils'; 3 + 4 + const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => ( 5 + <div 6 + ref={ref} 7 + className={cn('rounded-xl border border-border/70 bg-card/95 text-card-foreground shadow-sm backdrop-blur', className)} 8 + {...props} 9 + /> 10 + )); 11 + Card.displayName = 'Card'; 12 + 13 + const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => ( 14 + <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} /> 15 + )); 16 + CardHeader.displayName = 'CardHeader'; 17 + 18 + const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>( 19 + ({ className, ...props }, ref) => <h3 ref={ref} className={cn('text-lg font-semibold tracking-tight', className)} {...props} />, 20 + ); 21 + CardTitle.displayName = 'CardTitle'; 22 + 23 + const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>( 24 + ({ className, ...props }, ref) => <p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />, 25 + ); 26 + CardDescription.displayName = 'CardDescription'; 27 + 28 + const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => ( 29 + <div ref={ref} className={cn('p-6 pt-0', className)} {...props} /> 30 + )); 31 + CardContent.displayName = 'CardContent'; 32 + 33 + const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => ( 34 + <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} /> 35 + )); 36 + CardFooter.displayName = 'CardFooter'; 37 + 38 + export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
+19
web/src/components/ui/input.tsx
··· 1 + import * as React from 'react'; 2 + import { cn } from '../../lib/utils'; 3 + 4 + const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(({ className, type, ...props }, ref) => { 5 + return ( 6 + <input 7 + type={type} 8 + className={cn( 9 + 'flex h-10 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', 10 + className, 11 + )} 12 + ref={ref} 13 + {...props} 14 + /> 15 + ); 16 + }); 17 + Input.displayName = 'Input'; 18 + 19 + export { Input };
+9
web/src/components/ui/label.tsx
··· 1 + import * as React from 'react'; 2 + import { cn } from '../../lib/utils'; 3 + 4 + const Label = React.forwardRef<HTMLLabelElement, React.ComponentProps<'label'>>(({ className, ...props }, ref) => ( 5 + <label ref={ref} className={cn('text-sm font-medium leading-none text-muted-foreground', className)} {...props} /> 6 + )); 7 + Label.displayName = 'Label'; 8 + 9 + export { Label };
+72
web/src/index.css
··· 1 + @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Space+Grotesk:wght@400;500;600;700&display=swap'); 2 + 3 + @tailwind base; 4 + @tailwind components; 5 + @tailwind utilities; 6 + 7 + @layer base { 8 + :root { 9 + --background: 0 0% 99%; 10 + --foreground: 0 0% 8%; 11 + --card: 0 0% 100%; 12 + --card-foreground: 0 0% 8%; 13 + --primary: 0 0% 10%; 14 + --primary-foreground: 0 0% 98%; 15 + --secondary: 0 0% 94%; 16 + --secondary-foreground: 0 0% 16%; 17 + --muted: 0 0% 96%; 18 + --muted-foreground: 0 0% 40%; 19 + --border: 0 0% 86%; 20 + --input: 0 0% 86%; 21 + --ring: 0 0% 25%; 22 + } 23 + 24 + .dark { 25 + --background: 0 0% 6%; 26 + --foreground: 0 0% 95%; 27 + --card: 0 0% 9%; 28 + --card-foreground: 0 0% 95%; 29 + --primary: 0 0% 96%; 30 + --primary-foreground: 0 0% 8%; 31 + --secondary: 0 0% 16%; 32 + --secondary-foreground: 0 0% 96%; 33 + --muted: 0 0% 14%; 34 + --muted-foreground: 0 0% 70%; 35 + --border: 0 0% 22%; 36 + --input: 0 0% 22%; 37 + --ring: 0 0% 70%; 38 + } 39 + 40 + * { 41 + @apply border-border; 42 + } 43 + 44 + html, 45 + body, 46 + #root { 47 + @apply min-h-screen; 48 + } 49 + 50 + body { 51 + @apply bg-background text-foreground antialiased; 52 + font-family: 'Space Grotesk', system-ui, sans-serif; 53 + background-image: 54 + radial-gradient(circle at 12% 18%, rgba(0, 0, 0, 0.06) 0, transparent 24%), 55 + radial-gradient(circle at 84% 0%, rgba(0, 0, 0, 0.07) 0, transparent 28%), 56 + linear-gradient(140deg, rgba(255, 255, 255, 0.85), rgba(245, 245, 245, 0.5)); 57 + background-attachment: fixed; 58 + } 59 + 60 + .dark body { 61 + background-image: 62 + radial-gradient(circle at 10% 14%, rgba(255, 255, 255, 0.08) 0, transparent 24%), 63 + radial-gradient(circle at 85% 0%, rgba(255, 255, 255, 0.06) 0, transparent 24%), 64 + linear-gradient(145deg, rgba(11, 11, 11, 0.95), rgba(18, 18, 18, 0.86)); 65 + } 66 + } 67 + 68 + @layer components { 69 + .panel-grid { 70 + @apply grid gap-6 lg:grid-cols-[2fr_1fr]; 71 + } 72 + }
+6
web/src/lib/utils.ts
··· 1 + import { type ClassValue, clsx } from 'clsx'; 2 + import { twMerge } from 'tailwind-merge'; 3 + 4 + export function cn(...inputs: ClassValue[]) { 5 + return twMerge(clsx(inputs)); 6 + }
+5
web/src/main.tsx
··· 1 + import { createRoot } from 'react-dom/client'; 2 + import App from './App'; 3 + import './index.css'; 4 + 5 + createRoot(document.getElementById('root')!).render(<App />);
+1
web/src/vite-env.d.ts
··· 1 + /// <reference types="vite/client" />
+53
web/tailwind.config.cjs
··· 1 + /** @type {import('tailwindcss').Config} */ 2 + const path = require('node:path'); 3 + 4 + module.exports = { 5 + darkMode: ['class'], 6 + content: [path.join(__dirname, 'index.html'), path.join(__dirname, 'src/**/*.{ts,tsx}')], 7 + theme: { 8 + extend: { 9 + colors: { 10 + border: 'hsl(var(--border))', 11 + input: 'hsl(var(--input))', 12 + ring: 'hsl(var(--ring))', 13 + background: 'hsl(var(--background))', 14 + foreground: 'hsl(var(--foreground))', 15 + primary: { 16 + DEFAULT: 'hsl(var(--primary))', 17 + foreground: 'hsl(var(--primary-foreground))', 18 + }, 19 + secondary: { 20 + DEFAULT: 'hsl(var(--secondary))', 21 + foreground: 'hsl(var(--secondary-foreground))', 22 + }, 23 + muted: { 24 + DEFAULT: 'hsl(var(--muted))', 25 + foreground: 'hsl(var(--muted-foreground))', 26 + }, 27 + card: { 28 + DEFAULT: 'hsl(var(--card))', 29 + foreground: 'hsl(var(--card-foreground))', 30 + }, 31 + }, 32 + fontFamily: { 33 + sans: ['Space Grotesk', 'system-ui', 'sans-serif'], 34 + mono: ['JetBrains Mono', 'ui-monospace', 'monospace'], 35 + }, 36 + keyframes: { 37 + 'fade-in': { 38 + from: { opacity: '0' }, 39 + to: { opacity: '1' }, 40 + }, 41 + 'slide-up': { 42 + from: { opacity: '0', transform: 'translateY(8px)' }, 43 + to: { opacity: '1', transform: 'translateY(0)' }, 44 + }, 45 + }, 46 + animation: { 47 + 'fade-in': 'fade-in 320ms ease-out', 48 + 'slide-up': 'slide-up 420ms ease-out', 49 + }, 50 + }, 51 + }, 52 + plugins: [], 53 + };
+19
web/tsconfig.json
··· 1 + { 2 + "$schema": "https://json.schemastore.org/tsconfig", 3 + "compilerOptions": { 4 + "target": "ES2022", 5 + "lib": ["ES2022", "DOM", "DOM.Iterable"], 6 + "module": "ESNext", 7 + "moduleResolution": "Bundler", 8 + "jsx": "react-jsx", 9 + "allowJs": false, 10 + "strict": true, 11 + "noUnusedLocals": true, 12 + "noUnusedParameters": true, 13 + "esModuleInterop": true, 14 + "allowSyntheticDefaultImports": true, 15 + "skipLibCheck": true, 16 + "types": ["vite/client"] 17 + }, 18 + "include": ["./src"] 19 + }