diff --git a/job-extractor/src/main.ts b/job-extractor/src/main.ts
index 8dcdf5d..25d1a3c 100644
--- a/job-extractor/src/main.ts
+++ b/job-extractor/src/main.ts
@@ -19,7 +19,7 @@ const crawler = new PlaywrightCrawler({
maxRequestsPerCrawl: 20,
// Add delay between requests to slow down the process
minConcurrency: 1,
- maxConcurrency: 2,
+ maxConcurrency: 5,
navigationTimeoutSecs: 60,
// Add delay between requests (in milliseconds)
// requestHandlerTimeoutSecs: 50,
@@ -30,7 +30,7 @@ const crawler = new PlaywrightCrawler({
launchContext: {
launcher: firefox,
launchOptions: await launchOptions({
- headless: false,
+ headless: true,
// block_images: true,
// Pass your own Camoufox parameters here...
// block_images: true,
diff --git a/orchestrator/.env.example b/orchestrator/.env.example
new file mode 100644
index 0000000..fc94358
--- /dev/null
+++ b/orchestrator/.env.example
@@ -0,0 +1,21 @@
+# Server
+PORT=3001
+
+# OpenRouter API (for AI features)
+OPENROUTER_API_KEY=your_openrouter_api_key_here
+MODEL=openai/gpt-4o-mini
+
+# Notion integration (optional)
+NOTION_API_KEY=
+NOTION_DATABASE_ID=
+
+# Webhook security (optional)
+WEBHOOK_SECRET=
+
+# Pipeline configuration
+PIPELINE_TOP_N=10
+PIPELINE_MIN_SCORE=50
+
+# RXResume credentials (for PDF generation)
+RXRESUME_EMAIL=
+RXRESUME_PASSWORD=
diff --git a/orchestrator/.gitignore b/orchestrator/.gitignore
new file mode 100644
index 0000000..bfdfb16
--- /dev/null
+++ b/orchestrator/.gitignore
@@ -0,0 +1,26 @@
+# Dependencies
+node_modules/
+
+# Build output
+dist/
+
+# Data (local database and generated files)
+data/
+
+# Environment
+.env
+.env.local
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# IDE
+.idea/
+.vscode/
+*.swp
+*.swo
+
+# Logs
+*.log
+npm-debug.log*
diff --git a/orchestrator/README.md b/orchestrator/README.md
new file mode 100644
index 0000000..104264e
--- /dev/null
+++ b/orchestrator/README.md
@@ -0,0 +1,123 @@
+# Job Ops Orchestrator
+
+A unified orchestrator for the job application pipeline. Discovers jobs, scores them for suitability, generates tailored resumes, and provides a UI to manage applications.
+
+## Architecture
+
+```
+orchestrator/
+├── src/
+│ ├── server/ # Express backend
+│ │ ├── api/ # REST API routes
+│ │ ├── db/ # SQLite + Drizzle ORM
+│ │ ├── pipeline/ # Orchestration logic
+│ │ ├── repositories/ # Data access layer
+│ │ └── services/ # Integrations (crawler, AI, PDF)
+│ ├── client/ # React frontend
+│ │ ├── api/ # API client
+│ │ ├── components/ # UI components
+│ │ └── styles/ # CSS design system
+│ └── shared/ # Shared types
+├── data/ # SQLite DB + generated PDFs (gitignored)
+└── public/ # Static assets
+```
+
+## Setup
+
+1. **Install dependencies:**
+ ```bash
+ cd orchestrator
+ npm install
+ ```
+
+2. **Set up environment:**
+ ```bash
+ cp .env.example .env
+ # Edit .env with your API keys
+ ```
+
+3. **Initialize database:**
+ ```bash
+ npm run db:migrate
+ ```
+
+4. **Start development server:**
+ ```bash
+ npm run dev
+ ```
+
+ This starts:
+ - Backend API at `http://localhost:3001`
+ - Frontend at `http://localhost:5173`
+
+## API Endpoints
+
+### Jobs
+
+| Method | Endpoint | Description |
+|--------|----------|-------------|
+| GET | `/api/jobs` | List all jobs (filter with `?status=ready,discovered`) |
+| GET | `/api/jobs/:id` | Get single job |
+| PATCH | `/api/jobs/:id` | Update job |
+| POST | `/api/jobs/:id/process` | Generate resume for job |
+| POST | `/api/jobs/:id/apply` | Mark as applied + sync to Notion |
+| POST | `/api/jobs/:id/reject` | Mark as rejected |
+
+### Pipeline
+
+| Method | Endpoint | Description |
+|--------|----------|-------------|
+| GET | `/api/pipeline/status` | Get pipeline status |
+| GET | `/api/pipeline/runs` | Get recent pipeline runs |
+| POST | `/api/pipeline/run` | Trigger pipeline manually |
+| POST | `/api/webhook/trigger` | Webhook for n8n (use `WEBHOOK_SECRET`) |
+
+## Daily Flow
+
+1. **17:00 - n8n triggers pipeline:**
+ - Calls `POST /api/webhook/trigger`
+ - Pipeline crawls Gradcracker
+ - Scores jobs with AI
+ - Generates tailored resumes for top 10
+
+2. **You review in the UI:**
+ - See jobs at `http://localhost:5173`
+ - "Ready" tab shows jobs with generated PDFs
+ - Click "View Job" to open application
+ - Download PDF and apply manually
+ - Click "Mark Applied" → syncs to Notion
+
+## n8n Setup
+
+Create a workflow with:
+
+1. **Schedule Trigger** - Every day at 17:00
+2. **HTTP Request:**
+ - Method: POST
+ - URL: `http://localhost:3001/api/webhook/trigger`
+ - Headers: `Authorization: Bearer YOUR_WEBHOOK_SECRET`
+
+## Development
+
+```bash
+# Run just the server
+npm run dev:server
+
+# Run just the client
+npm run dev:client
+
+# Run the pipeline manually
+npm run pipeline:run
+
+# Build for production
+npm run build
+npm start
+```
+
+## Tech Stack
+
+- **Backend:** Express, TypeScript, Drizzle ORM, SQLite
+- **Frontend:** React, Vite, CSS (custom design system)
+- **AI:** OpenRouter API (GPT-4o-mini)
+- **PDF Generation:** Wraps existing Python RXResume automation
+- **Job Crawling:** Wraps existing TypeScript Crawlee crawler
diff --git a/orchestrator/index.html b/orchestrator/index.html
new file mode 100644
index 0000000..409f757
--- /dev/null
+++ b/orchestrator/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ Job Ops | Orchestrator
+
+
+
+
+
+
+
+
+
diff --git a/orchestrator/package-lock.json b/orchestrator/package-lock.json
new file mode 100644
index 0000000..3140b91
--- /dev/null
+++ b/orchestrator/package-lock.json
@@ -0,0 +1,5403 @@
+{
+ "name": "job-ops-orchestrator",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "job-ops-orchestrator",
+ "version": "1.0.0",
+ "dependencies": {
+ "better-sqlite3": "^11.6.0",
+ "cors": "^2.8.5",
+ "dotenv": "^17.2.3",
+ "drizzle-orm": "^0.38.2",
+ "express": "^4.18.2",
+ "zod": "^3.23.8"
+ },
+ "devDependencies": {
+ "@types/better-sqlite3": "^7.6.8",
+ "@types/cors": "^2.8.17",
+ "@types/express": "^4.17.21",
+ "@types/node": "^22.10.1",
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "concurrently": "^9.1.0",
+ "drizzle-kit": "^0.30.1",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^7.0.2",
+ "tsx": "^4.19.2",
+ "typescript": "^5.7.2",
+ "vite": "^6.0.3"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@drizzle-team/brocli": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz",
+ "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@esbuild-kit/core-utils": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz",
+ "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==",
+ "deprecated": "Merged into tsx: https://tsx.is",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.18.20",
+ "source-map-support": "^0.5.21"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+ "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+ "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+ "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+ "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+ "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+ "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+ "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+ "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+ "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+ "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+ "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+ "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+ "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+ "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+ "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+ "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+ "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+ "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+ "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+ "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
+ "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.18.20",
+ "@esbuild/android-arm64": "0.18.20",
+ "@esbuild/android-x64": "0.18.20",
+ "@esbuild/darwin-arm64": "0.18.20",
+ "@esbuild/darwin-x64": "0.18.20",
+ "@esbuild/freebsd-arm64": "0.18.20",
+ "@esbuild/freebsd-x64": "0.18.20",
+ "@esbuild/linux-arm": "0.18.20",
+ "@esbuild/linux-arm64": "0.18.20",
+ "@esbuild/linux-ia32": "0.18.20",
+ "@esbuild/linux-loong64": "0.18.20",
+ "@esbuild/linux-mips64el": "0.18.20",
+ "@esbuild/linux-ppc64": "0.18.20",
+ "@esbuild/linux-riscv64": "0.18.20",
+ "@esbuild/linux-s390x": "0.18.20",
+ "@esbuild/linux-x64": "0.18.20",
+ "@esbuild/netbsd-x64": "0.18.20",
+ "@esbuild/openbsd-x64": "0.18.20",
+ "@esbuild/sunos-x64": "0.18.20",
+ "@esbuild/win32-arm64": "0.18.20",
+ "@esbuild/win32-ia32": "0.18.20",
+ "@esbuild/win32-x64": "0.18.20"
+ }
+ },
+ "node_modules/@esbuild-kit/esm-loader": {
+ "version": "2.6.5",
+ "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz",
+ "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==",
+ "deprecated": "Merged into tsx: https://tsx.is",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@esbuild-kit/core-utils": "^3.3.2",
+ "get-tsconfig": "^4.7.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
+ "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
+ "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
+ "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
+ "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
+ "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
+ "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
+ "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
+ "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
+ "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
+ "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
+ "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
+ "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
+ "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
+ "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
+ "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
+ "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
+ "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz",
+ "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
+ "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz",
+ "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
+ "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz",
+ "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
+ "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
+ "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
+ "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
+ "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@petamoriken/float16": {
+ "version": "3.9.3",
+ "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.3.tgz",
+ "integrity": "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.27",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
+ "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
+ "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
+ "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
+ "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
+ "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
+ "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
+ "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
+ "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
+ "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
+ "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
+ "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
+ "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
+ "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
+ "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
+ "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
+ "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
+ "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
+ "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
+ "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+ "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/better-sqlite3": {
+ "version": "7.6.13",
+ "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
+ "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.6",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
+ "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/cors": {
+ "version": "2.8.19",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
+ "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.25",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz",
+ "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.33",
+ "@types/qs": "*",
+ "@types/serve-static": "^1"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.19.7",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz",
+ "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*",
+ "@types/send": "*"
+ }
+ },
+ "node_modules/@types/http-errors": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/mime": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
+ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.19.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz",
+ "integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.15",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.27",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
+ "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
+ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^18.0.0"
+ }
+ },
+ "node_modules/@types/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.15.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz",
+ "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/node": "*",
+ "@types/send": "<1"
+ }
+ },
+ "node_modules/@types/serve-static/node_modules/@types/send": {
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz",
+ "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.6",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.6.tgz",
+ "integrity": "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/better-sqlite3": {
+ "version": "11.10.0",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
+ "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "prebuild-install": "^7.1.1"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "license": "MIT",
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
+ "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.14.0",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001760",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
+ "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "license": "ISC"
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concurrently": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
+ "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "4.1.2",
+ "rxjs": "7.8.2",
+ "shell-quote": "1.8.3",
+ "supports-color": "8.1.1",
+ "tree-kill": "1.2.2",
+ "yargs": "17.7.2"
+ },
+ "bin": {
+ "conc": "dist/bin/concurrently.js",
+ "concurrently": "dist/bin/concurrently.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "license": "MIT"
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "17.2.3",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
+ "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/drizzle-kit": {
+ "version": "0.30.6",
+ "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.30.6.tgz",
+ "integrity": "sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@drizzle-team/brocli": "^0.10.2",
+ "@esbuild-kit/esm-loader": "^2.5.5",
+ "esbuild": "^0.19.7",
+ "esbuild-register": "^3.5.0",
+ "gel": "^2.0.0"
+ },
+ "bin": {
+ "drizzle-kit": "bin.cjs"
+ }
+ },
+ "node_modules/drizzle-orm": {
+ "version": "0.38.4",
+ "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.38.4.tgz",
+ "integrity": "sha512-s7/5BpLKO+WJRHspvpqTydxFob8i1vo2rEx4pY6TGY7QSMuUfWUuzaY0DIpXCkgHOo37BaFC+SJQb99dDUXT3Q==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@aws-sdk/client-rds-data": ">=3",
+ "@cloudflare/workers-types": ">=4",
+ "@electric-sql/pglite": ">=0.2.0",
+ "@libsql/client": ">=0.10.0",
+ "@libsql/client-wasm": ">=0.10.0",
+ "@neondatabase/serverless": ">=0.10.0",
+ "@op-engineering/op-sqlite": ">=2",
+ "@opentelemetry/api": "^1.4.1",
+ "@planetscale/database": ">=1",
+ "@prisma/client": "*",
+ "@tidbcloud/serverless": "*",
+ "@types/better-sqlite3": "*",
+ "@types/pg": "*",
+ "@types/react": ">=18",
+ "@types/sql.js": "*",
+ "@vercel/postgres": ">=0.8.0",
+ "@xata.io/client": "*",
+ "better-sqlite3": ">=7",
+ "bun-types": "*",
+ "expo-sqlite": ">=14.0.0",
+ "knex": "*",
+ "kysely": "*",
+ "mysql2": ">=2",
+ "pg": ">=8",
+ "postgres": ">=3",
+ "react": ">=18",
+ "sql.js": ">=1",
+ "sqlite3": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/client-rds-data": {
+ "optional": true
+ },
+ "@cloudflare/workers-types": {
+ "optional": true
+ },
+ "@electric-sql/pglite": {
+ "optional": true
+ },
+ "@libsql/client": {
+ "optional": true
+ },
+ "@libsql/client-wasm": {
+ "optional": true
+ },
+ "@neondatabase/serverless": {
+ "optional": true
+ },
+ "@op-engineering/op-sqlite": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@planetscale/database": {
+ "optional": true
+ },
+ "@prisma/client": {
+ "optional": true
+ },
+ "@tidbcloud/serverless": {
+ "optional": true
+ },
+ "@types/better-sqlite3": {
+ "optional": true
+ },
+ "@types/pg": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ },
+ "@types/sql.js": {
+ "optional": true
+ },
+ "@vercel/postgres": {
+ "optional": true
+ },
+ "@xata.io/client": {
+ "optional": true
+ },
+ "better-sqlite3": {
+ "optional": true
+ },
+ "bun-types": {
+ "optional": true
+ },
+ "expo-sqlite": {
+ "optional": true
+ },
+ "knex": {
+ "optional": true
+ },
+ "kysely": {
+ "optional": true
+ },
+ "mysql2": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ },
+ "postgres": {
+ "optional": true
+ },
+ "prisma": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "sql.js": {
+ "optional": true
+ },
+ "sqlite3": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
+ "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.19.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
+ "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.19.12",
+ "@esbuild/android-arm": "0.19.12",
+ "@esbuild/android-arm64": "0.19.12",
+ "@esbuild/android-x64": "0.19.12",
+ "@esbuild/darwin-arm64": "0.19.12",
+ "@esbuild/darwin-x64": "0.19.12",
+ "@esbuild/freebsd-arm64": "0.19.12",
+ "@esbuild/freebsd-x64": "0.19.12",
+ "@esbuild/linux-arm": "0.19.12",
+ "@esbuild/linux-arm64": "0.19.12",
+ "@esbuild/linux-ia32": "0.19.12",
+ "@esbuild/linux-loong64": "0.19.12",
+ "@esbuild/linux-mips64el": "0.19.12",
+ "@esbuild/linux-ppc64": "0.19.12",
+ "@esbuild/linux-riscv64": "0.19.12",
+ "@esbuild/linux-s390x": "0.19.12",
+ "@esbuild/linux-x64": "0.19.12",
+ "@esbuild/netbsd-x64": "0.19.12",
+ "@esbuild/openbsd-x64": "0.19.12",
+ "@esbuild/sunos-x64": "0.19.12",
+ "@esbuild/win32-arm64": "0.19.12",
+ "@esbuild/win32-ia32": "0.19.12",
+ "@esbuild/win32-x64": "0.19.12"
+ }
+ },
+ "node_modules/esbuild-register": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz",
+ "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
+ "peerDependencies": {
+ "esbuild": ">=0.12 <1"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "license": "(MIT OR WTFPL)",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
+ "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.3",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.14.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "license": "MIT"
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "license": "MIT"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gel": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/gel/-/gel-2.2.0.tgz",
+ "integrity": "sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@petamoriken/float16": "^3.8.7",
+ "debug": "^4.3.4",
+ "env-paths": "^3.0.0",
+ "semver": "^7.6.2",
+ "shell-quote": "^1.8.1",
+ "which": "^4.0.0"
+ },
+ "bin": {
+ "gel": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ }
+ },
+ "node_modules/gel/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+ "license": "MIT"
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+ "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "license": "MIT"
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-build-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
+ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-abi": {
+ "version": "3.85.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz",
+ "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==",
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-abi/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prebuild-install": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
+ "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.0",
+ "expand-template": "^2.0.3",
+ "github-from-package": "0.0.0",
+ "minimist": "^1.2.3",
+ "mkdirp-classic": "^0.5.3",
+ "napi-build-utils": "^2.0.0",
+ "node-abi": "^3.3.0",
+ "pump": "^3.0.0",
+ "rc": "^1.2.7",
+ "simple-get": "^4.0.0",
+ "tar-fs": "^2.0.0",
+ "tunnel-agent": "^0.6.0"
+ },
+ "bin": {
+ "prebuild-install": "bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pump": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.1.tgz",
+ "integrity": "sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.10.1.tgz",
+ "integrity": "sha512-JNBANI6ChGVjA5bwsUIwJk7LHKmqB4JYnYfzFwyp2t12Izva11elds2jx7Yfoup2zssedntwU0oZ5DEmk5Sdaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.10.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/react-router/node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
+ "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.53.3",
+ "@rollup/rollup-android-arm64": "4.53.3",
+ "@rollup/rollup-darwin-arm64": "4.53.3",
+ "@rollup/rollup-darwin-x64": "4.53.3",
+ "@rollup/rollup-freebsd-arm64": "4.53.3",
+ "@rollup/rollup-freebsd-x64": "4.53.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.53.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.53.3",
+ "@rollup/rollup-linux-arm64-musl": "4.53.3",
+ "@rollup/rollup-linux-loong64-gnu": "4.53.3",
+ "@rollup/rollup-linux-ppc64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-musl": "4.53.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-musl": "4.53.3",
+ "@rollup/rollup-openharmony-arm64": "4.53.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.53.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.53.3",
+ "@rollup/rollup-win32-x64-gnu": "4.53.3",
+ "@rollup/rollup-win32-x64-msvc": "4.53.3",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.1.tgz",
+ "integrity": "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/send/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/send/node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/send/node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
+ "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.19.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/serve-static/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/serve-static/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/serve-static/node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/serve-static/node_modules/send": {
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
+ "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/serve-static/node_modules/send/node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/serve-static/node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/shell-quote": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
+ "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/simple-get": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decompress-response": "^6.0.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
+ "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/tsx": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz",
+ "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz",
+ "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz",
+ "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz",
+ "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz",
+ "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz",
+ "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz",
+ "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz",
+ "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz",
+ "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz",
+ "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz",
+ "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz",
+ "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz",
+ "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz",
+ "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz",
+ "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz",
+ "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz",
+ "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz",
+ "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz",
+ "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz",
+ "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz",
+ "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz",
+ "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz",
+ "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/esbuild": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz",
+ "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.1",
+ "@esbuild/android-arm": "0.27.1",
+ "@esbuild/android-arm64": "0.27.1",
+ "@esbuild/android-x64": "0.27.1",
+ "@esbuild/darwin-arm64": "0.27.1",
+ "@esbuild/darwin-x64": "0.27.1",
+ "@esbuild/freebsd-arm64": "0.27.1",
+ "@esbuild/freebsd-x64": "0.27.1",
+ "@esbuild/linux-arm": "0.27.1",
+ "@esbuild/linux-arm64": "0.27.1",
+ "@esbuild/linux-ia32": "0.27.1",
+ "@esbuild/linux-loong64": "0.27.1",
+ "@esbuild/linux-mips64el": "0.27.1",
+ "@esbuild/linux-ppc64": "0.27.1",
+ "@esbuild/linux-riscv64": "0.27.1",
+ "@esbuild/linux-s390x": "0.27.1",
+ "@esbuild/linux-x64": "0.27.1",
+ "@esbuild/netbsd-arm64": "0.27.1",
+ "@esbuild/netbsd-x64": "0.27.1",
+ "@esbuild/openbsd-arm64": "0.27.1",
+ "@esbuild/openbsd-x64": "0.27.1",
+ "@esbuild/openharmony-arm64": "0.27.1",
+ "@esbuild/sunos-x64": "0.27.1",
+ "@esbuild/win32-arm64": "0.27.1",
+ "@esbuild/win32-ia32": "0.27.1",
+ "@esbuild/win32-x64": "0.27.1"
+ }
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
+ "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
+ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/which": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+ "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^3.1.1"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/orchestrator/package.json b/orchestrator/package.json
new file mode 100644
index 0000000..01b7450
--- /dev/null
+++ b/orchestrator/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "job-ops-orchestrator",
+ "version": "1.0.0",
+ "type": "module",
+ "description": "Unified orchestrator for job application pipeline",
+ "main": "dist/server/index.js",
+ "scripts": {
+ "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
+ "dev:server": "tsx watch src/server/index.ts",
+ "dev:client": "vite",
+ "build": "npm run build:client && npm run build:server",
+ "build:server": "tsc -p tsconfig.server.json",
+ "build:client": "vite build",
+ "start": "node dist/server/index.js",
+ "db:migrate": "tsx src/server/db/migrate.ts",
+ "pipeline:run": "tsx src/server/pipeline/run.ts"
+ },
+ "dependencies": {
+ "better-sqlite3": "^11.6.0",
+ "cors": "^2.8.5",
+ "dotenv": "^17.2.3",
+ "drizzle-orm": "^0.38.2",
+ "express": "^4.18.2",
+ "zod": "^3.23.8"
+ },
+ "devDependencies": {
+ "@types/better-sqlite3": "^7.6.8",
+ "@types/cors": "^2.8.17",
+ "@types/express": "^4.17.21",
+ "@types/node": "^22.10.1",
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "concurrently": "^9.1.0",
+ "drizzle-kit": "^0.30.1",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^7.0.2",
+ "tsx": "^4.19.2",
+ "typescript": "^5.7.2",
+ "vite": "^6.0.3"
+ }
+}
diff --git a/orchestrator/src/client/App.tsx b/orchestrator/src/client/App.tsx
new file mode 100644
index 0000000..d39096a
--- /dev/null
+++ b/orchestrator/src/client/App.tsx
@@ -0,0 +1,162 @@
+/**
+ * Main App component.
+ */
+
+import React, { useState, useEffect, useCallback } from 'react';
+import type { Job, JobStatus } from '../shared/types';
+import { Header, Stats, JobList, ToastContainer, Toast } from './components';
+import * as api from './api';
+
+export const App: React.FC = () => {
+ // State
+ const [jobs, setJobs] = useState([]);
+ const [stats, setStats] = useState>({
+ discovered: 0,
+ processing: 0,
+ ready: 0,
+ applied: 0,
+ rejected: 0,
+ expired: 0,
+ });
+ const [isLoading, setIsLoading] = useState(true);
+ const [isPipelineRunning, setIsPipelineRunning] = useState(false);
+ const [processingJobId, setProcessingJobId] = useState(null);
+ const [toasts, setToasts] = useState([]);
+
+ // Toast helpers
+ const addToast = useCallback((message: string, type: Toast['type']) => {
+ const id = Math.random().toString(36).slice(2);
+ setToasts(prev => [...prev, { id, message, type }]);
+ }, []);
+
+ const dismissToast = useCallback((id: string) => {
+ setToasts(prev => prev.filter(t => t.id !== id));
+ }, []);
+
+ // Load jobs
+ const loadJobs = useCallback(async () => {
+ try {
+ setIsLoading(true);
+ const data = await api.getJobs();
+ setJobs(data.jobs);
+ setStats(data.byStatus);
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Failed to load jobs';
+ addToast(message, 'error');
+ } finally {
+ setIsLoading(false);
+ }
+ }, [addToast]);
+
+ // Check pipeline status
+ const checkPipelineStatus = useCallback(async () => {
+ try {
+ const status = await api.getPipelineStatus();
+ setIsPipelineRunning(status.isRunning);
+ } catch {
+ // Ignore errors
+ }
+ }, []);
+
+ // Initial load
+ useEffect(() => {
+ loadJobs();
+ checkPipelineStatus();
+
+ // Poll for updates
+ const interval = setInterval(() => {
+ loadJobs();
+ checkPipelineStatus();
+ }, 10000);
+
+ return () => clearInterval(interval);
+ }, [loadJobs, checkPipelineStatus]);
+
+ // Run pipeline
+ const handleRunPipeline = async () => {
+ try {
+ setIsPipelineRunning(true);
+ await api.runPipeline();
+ addToast('Pipeline started! This may take a few minutes.', 'info');
+
+ // Poll more frequently while running
+ const pollInterval = setInterval(async () => {
+ const status = await api.getPipelineStatus();
+ if (!status.isRunning) {
+ clearInterval(pollInterval);
+ setIsPipelineRunning(false);
+ loadJobs();
+ addToast('Pipeline completed!', 'success');
+ }
+ }, 5000);
+ } catch (error) {
+ setIsPipelineRunning(false);
+ const message = error instanceof Error ? error.message : 'Failed to start pipeline';
+ addToast(message, 'error');
+ }
+ };
+
+ // Process single job
+ const handleProcess = async (jobId: string) => {
+ try {
+ setProcessingJobId(jobId);
+ await api.processJob(jobId);
+ addToast('Resume generated successfully!', 'success');
+ loadJobs();
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Failed to process job';
+ addToast(message, 'error');
+ } finally {
+ setProcessingJobId(null);
+ }
+ };
+
+ // Mark as applied
+ const handleApply = async (jobId: string) => {
+ try {
+ await api.markAsApplied(jobId);
+ addToast('Marked as applied! ✅', 'success');
+ loadJobs();
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Failed to mark as applied';
+ addToast(message, 'error');
+ }
+ };
+
+ // Reject job
+ const handleReject = async (jobId: string) => {
+ try {
+ await api.rejectJob(jobId);
+ addToast('Job skipped', 'info');
+ loadJobs();
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Failed to reject job';
+ addToast(message, 'error');
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/orchestrator/src/client/api/client.ts b/orchestrator/src/client/api/client.ts
new file mode 100644
index 0000000..0d470e3
--- /dev/null
+++ b/orchestrator/src/client/api/client.ts
@@ -0,0 +1,91 @@
+/**
+ * API client for the orchestrator backend.
+ */
+
+import type {
+ Job,
+ ApiResponse,
+ JobsListResponse,
+ PipelineStatusResponse,
+ PipelineRun
+} from '../../shared/types';
+
+const API_BASE = '/api';
+
+async function fetchApi(
+ endpoint: string,
+ options?: RequestInit
+): Promise {
+ const response = await fetch(`${API_BASE}${endpoint}`, {
+ ...options,
+ headers: {
+ 'Content-Type': 'application/json',
+ ...options?.headers,
+ },
+ });
+
+ const data: ApiResponse = await response.json();
+
+ if (!data.success) {
+ throw new Error(data.error || 'API request failed');
+ }
+
+ return data.data as T;
+}
+
+// Jobs API
+export async function getJobs(statuses?: string[]): Promise {
+ const query = statuses?.length ? `?status=${statuses.join(',')}` : '';
+ return fetchApi(`/jobs${query}`);
+}
+
+export async function getJob(id: string): Promise {
+ return fetchApi(`/jobs/${id}`);
+}
+
+export async function updateJob(
+ id: string,
+ update: Partial
+): Promise {
+ return fetchApi(`/jobs/${id}`, {
+ method: 'PATCH',
+ body: JSON.stringify(update),
+ });
+}
+
+export async function processJob(id: string): Promise {
+ return fetchApi(`/jobs/${id}/process`, {
+ method: 'POST',
+ });
+}
+
+export async function markAsApplied(id: string): Promise {
+ return fetchApi(`/jobs/${id}/apply`, {
+ method: 'POST',
+ });
+}
+
+export async function rejectJob(id: string): Promise {
+ return fetchApi(`/jobs/${id}/reject`, {
+ method: 'POST',
+ });
+}
+
+// Pipeline API
+export async function getPipelineStatus(): Promise {
+ return fetchApi('/pipeline/status');
+}
+
+export async function getPipelineRuns(): Promise {
+ return fetchApi('/pipeline/runs');
+}
+
+export async function runPipeline(config?: {
+ topN?: number;
+ minSuitabilityScore?: number;
+}): Promise<{ message: string }> {
+ return fetchApi<{ message: string }>('/pipeline/run', {
+ method: 'POST',
+ body: JSON.stringify(config || {}),
+ });
+}
diff --git a/orchestrator/src/client/api/index.ts b/orchestrator/src/client/api/index.ts
new file mode 100644
index 0000000..4f1cce4
--- /dev/null
+++ b/orchestrator/src/client/api/index.ts
@@ -0,0 +1 @@
+export * from './client';
diff --git a/orchestrator/src/client/components/Header.tsx b/orchestrator/src/client/components/Header.tsx
new file mode 100644
index 0000000..9890504
--- /dev/null
+++ b/orchestrator/src/client/components/Header.tsx
@@ -0,0 +1,64 @@
+/**
+ * Header component with logo and pipeline trigger.
+ */
+
+import React from 'react';
+import { RocketIcon, PlayIcon, RefreshIcon } from './Icons';
+
+interface HeaderProps {
+ onRunPipeline: () => void;
+ onRefresh: () => void;
+ isPipelineRunning: boolean;
+ isLoading: boolean;
+}
+
+export const Header: React.FC = ({
+ onRunPipeline,
+ onRefresh,
+ isPipelineRunning,
+ isLoading,
+}) => {
+ return (
+
+
+
+
+
+
+
+
+ Refresh
+
+
+
+ {isPipelineRunning ? (
+ <>
+
+ Running...
+ >
+ ) : (
+ <>
+
+ Run Pipeline
+ >
+ )}
+
+
+
+
+
+ );
+};
diff --git a/orchestrator/src/client/components/Icons.tsx b/orchestrator/src/client/components/Icons.tsx
new file mode 100644
index 0000000..24d33ce
--- /dev/null
+++ b/orchestrator/src/client/components/Icons.tsx
@@ -0,0 +1,118 @@
+/**
+ * SVG Icons as React components.
+ */
+
+import React from 'react';
+
+interface IconProps {
+ className?: string;
+ size?: number;
+}
+
+export const BriefcaseIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+);
+
+export const MapPinIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+);
+
+export const CalendarIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+
+
+);
+
+export const DollarIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+);
+
+export const GraduationCapIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+);
+
+export const ExternalLinkIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+
+);
+
+export const FileTextIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+
+
+
+);
+
+export const CheckCircleIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+);
+
+export const XCircleIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+
+);
+
+export const RefreshIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+
+);
+
+export const PlayIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+);
+
+export const DownloadIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+
+);
+
+export const XIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+);
+
+export const RocketIcon: React.FC = ({ className, size = 16 }) => (
+
+
+
+
+
+
+);
diff --git a/orchestrator/src/client/components/JobCard.tsx b/orchestrator/src/client/components/JobCard.tsx
new file mode 100644
index 0000000..1efb4cc
--- /dev/null
+++ b/orchestrator/src/client/components/JobCard.tsx
@@ -0,0 +1,174 @@
+/**
+ * Individual job card component.
+ */
+
+import React from 'react';
+import type { Job } from '../../shared/types';
+import { StatusBadge } from './StatusBadge';
+import { ScoreIndicator } from './ScoreIndicator';
+import {
+ MapPinIcon,
+ CalendarIcon,
+ DollarIcon,
+ GraduationCapIcon,
+ ExternalLinkIcon,
+ DownloadIcon,
+ CheckCircleIcon,
+ XCircleIcon,
+ RefreshIcon,
+} from './Icons';
+
+interface JobCardProps {
+ job: Job;
+ onApply: (id: string) => void;
+ onReject: (id: string) => void;
+ onProcess: (id: string) => void;
+ isProcessing: boolean;
+}
+
+export const JobCard: React.FC = ({
+ job,
+ onApply,
+ onReject,
+ onProcess,
+ isProcessing,
+}) => {
+ const formatDate = (dateStr: string | null) => {
+ if (!dateStr) return null;
+ try {
+ return new Date(dateStr).toLocaleDateString('en-GB', {
+ day: 'numeric',
+ month: 'short',
+ year: 'numeric',
+ });
+ } catch {
+ return dateStr;
+ }
+ };
+
+ const hasPdf = !!job.pdfPath;
+ const canApply = job.status === 'ready';
+ const canProcess = job.status === 'discovered';
+ const canReject = ['discovered', 'ready'].includes(job.status);
+
+ return (
+
+
+
+
{job.title}
+
{job.employer}
+
+
+
+
+
+
+
+
+ {job.location && (
+
+
+ {job.location}
+
+ )}
+ {job.deadline && (
+
+
+ {job.deadline}
+
+ )}
+ {job.salary && (
+
+
+ {job.salary}
+
+ )}
+ {job.degreeRequired && (
+
+
+ {job.degreeRequired}
+
+ )}
+
+
+ {job.suitabilityReason && (
+
+ "{job.suitabilityReason}"
+
+ )}
+
+
+ {/* View job posting */}
+
+
+ View Job
+
+
+ {/* Download PDF */}
+ {hasPdf && (
+
+
+ Download PDF
+
+ )}
+
+ {/* Process job */}
+ {canProcess && (
+
onProcess(job.id)}
+ disabled={isProcessing}
+ >
+ {isProcessing ? (
+ <>
+
+ Processing...
+ >
+ ) : (
+ <>
+
+ Generate Resume
+ >
+ )}
+
+ )}
+
+ {/* Reject */}
+ {canReject && (
+
onReject(job.id)}
+ >
+
+ Skip
+
+ )}
+
+ {/* Mark as applied */}
+ {canApply && (
+
onApply(job.id)}
+ >
+
+ Mark Applied
+
+ )}
+
+
+ );
+};
diff --git a/orchestrator/src/client/components/JobList.tsx b/orchestrator/src/client/components/JobList.tsx
new file mode 100644
index 0000000..2583d19
--- /dev/null
+++ b/orchestrator/src/client/components/JobList.tsx
@@ -0,0 +1,90 @@
+/**
+ * Job list with filtering tabs.
+ */
+
+import React, { useState } from 'react';
+import type { Job, JobStatus } from '../../shared/types';
+import { JobCard } from './JobCard';
+
+interface JobListProps {
+ jobs: Job[];
+ onApply: (id: string) => void;
+ onReject: (id: string) => void;
+ onProcess: (id: string) => void;
+ processingJobId: string | null;
+}
+
+type FilterTab = 'ready' | 'discovered' | 'applied' | 'all';
+
+const tabs: Array<{ id: FilterTab; label: string; statuses: JobStatus[] }> = [
+ { id: 'ready', label: '✨ Ready to Apply', statuses: ['ready'] },
+ { id: 'discovered', label: '🔍 Discovered', statuses: ['discovered', 'processing'] },
+ { id: 'applied', label: '✅ Applied', statuses: ['applied'] },
+ { id: 'all', label: '📋 All Jobs', statuses: [] },
+];
+
+export const JobList: React.FC = ({
+ jobs,
+ onApply,
+ onReject,
+ onProcess,
+ processingJobId,
+}) => {
+ const [activeTab, setActiveTab] = useState('ready');
+
+ const filteredJobs = React.useMemo(() => {
+ const tab = tabs.find(t => t.id === activeTab);
+ if (!tab || tab.statuses.length === 0) {
+ return jobs;
+ }
+ return jobs.filter(job => tab.statuses.includes(job.status));
+ }, [jobs, activeTab]);
+
+ return (
+
+
+ {tabs.map(tab => {
+ const count = tab.statuses.length === 0
+ ? jobs.length
+ : jobs.filter(j => tab.statuses.includes(j.status)).length;
+
+ return (
+ setActiveTab(tab.id)}
+ >
+ {tab.label} ({count})
+
+ );
+ })}
+
+
+ {filteredJobs.length === 0 ? (
+
+
📭
+
No jobs found
+
+ {activeTab === 'ready' && 'Run the pipeline to discover and process new jobs.'}
+ {activeTab === 'discovered' && 'All discovered jobs have been processed.'}
+ {activeTab === 'applied' && "You haven't applied to any jobs yet."}
+ {activeTab === 'all' && 'No jobs in the system yet. Run the pipeline to get started!'}
+
+
+ ) : (
+
+ {filteredJobs.map(job => (
+
+ ))}
+
+ )}
+
+ );
+};
diff --git a/orchestrator/src/client/components/ScoreIndicator.tsx b/orchestrator/src/client/components/ScoreIndicator.tsx
new file mode 100644
index 0000000..0d7ab54
--- /dev/null
+++ b/orchestrator/src/client/components/ScoreIndicator.tsx
@@ -0,0 +1,37 @@
+/**
+ * Suitability score display component.
+ */
+
+import React from 'react';
+
+interface ScoreIndicatorProps {
+ score: number | null;
+}
+
+export const ScoreIndicator: React.FC = ({ score }) => {
+ if (score === null) {
+ return (
+
+ Not scored
+
+ );
+ }
+
+ const getScoreClass = () => {
+ if (score >= 70) return 'score-high';
+ if (score >= 40) return 'score-medium';
+ return 'score-low';
+ };
+
+ return (
+
+ );
+};
diff --git a/orchestrator/src/client/components/Stats.tsx b/orchestrator/src/client/components/Stats.tsx
new file mode 100644
index 0000000..39e2230
--- /dev/null
+++ b/orchestrator/src/client/components/Stats.tsx
@@ -0,0 +1,50 @@
+/**
+ * Stats dashboard showing job counts by status.
+ */
+
+import React from 'react';
+import type { JobStatus } from '../../shared/types';
+
+interface StatsProps {
+ stats: Record;
+}
+
+const statConfig: Array<{
+ key: JobStatus;
+ label: string;
+ emoji: string;
+}> = [
+ { key: 'discovered', label: 'Discovered', emoji: '🔍' },
+ { key: 'processing', label: 'Processing', emoji: '⚙️' },
+ { key: 'ready', label: 'Ready', emoji: '✨' },
+ { key: 'applied', label: 'Applied', emoji: '✅' },
+ { key: 'rejected', label: 'Rejected', emoji: '❌' },
+ { key: 'expired', label: 'Expired', emoji: '⏰' },
+];
+
+export const Stats: React.FC = ({ stats }) => {
+ const total = Object.values(stats).reduce((a, b) => a + b, 0);
+
+ return (
+
+
+
Overview
+
+ {total} total jobs
+
+
+
+
+ {statConfig.map(({ key, label, emoji }) => (
+
+
{stats[key] || 0}
+
+ {emoji}
+ {label}
+
+
+ ))}
+
+
+ );
+};
diff --git a/orchestrator/src/client/components/StatusBadge.tsx b/orchestrator/src/client/components/StatusBadge.tsx
new file mode 100644
index 0000000..c7fe138
--- /dev/null
+++ b/orchestrator/src/client/components/StatusBadge.tsx
@@ -0,0 +1,28 @@
+/**
+ * Status badge component.
+ */
+
+import React from 'react';
+import type { JobStatus } from '../../shared/types';
+
+interface StatusBadgeProps {
+ status: JobStatus;
+}
+
+const statusLabels: Record = {
+ discovered: 'Discovered',
+ processing: 'Processing',
+ ready: 'Ready',
+ applied: 'Applied',
+ rejected: 'Rejected',
+ expired: 'Expired',
+};
+
+export const StatusBadge: React.FC = ({ status }) => {
+ return (
+
+ {status === 'processing' && ● }
+ {statusLabels[status]}
+
+ );
+};
diff --git a/orchestrator/src/client/components/Toast.tsx b/orchestrator/src/client/components/Toast.tsx
new file mode 100644
index 0000000..ae520ac
--- /dev/null
+++ b/orchestrator/src/client/components/Toast.tsx
@@ -0,0 +1,44 @@
+/**
+ * Toast notification component.
+ */
+
+import React, { useEffect } from 'react';
+
+export interface Toast {
+ id: string;
+ message: string;
+ type: 'success' | 'error' | 'info';
+}
+
+interface ToastContainerProps {
+ toasts: Toast[];
+ onDismiss: (id: string) => void;
+}
+
+export const ToastContainer: React.FC = ({ toasts, onDismiss }) => {
+ return (
+
+ {toasts.map(toast => (
+
+ ))}
+
+ );
+};
+
+const ToastItem: React.FC<{ toast: Toast; onDismiss: (id: string) => void }> = ({
+ toast,
+ onDismiss,
+}) => {
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ onDismiss(toast.id);
+ }, 5000);
+ return () => clearTimeout(timer);
+ }, [toast.id, onDismiss]);
+
+ return (
+
+ {toast.message}
+
+ );
+};
diff --git a/orchestrator/src/client/components/index.ts b/orchestrator/src/client/components/index.ts
new file mode 100644
index 0000000..362abdb
--- /dev/null
+++ b/orchestrator/src/client/components/index.ts
@@ -0,0 +1,8 @@
+export { Header } from './Header';
+export { Stats } from './Stats';
+export { StatusBadge } from './StatusBadge';
+export { ScoreIndicator } from './ScoreIndicator';
+export { JobCard } from './JobCard';
+export { JobList } from './JobList';
+export { ToastContainer, type Toast } from './Toast';
+export * from './Icons';
diff --git a/orchestrator/src/client/main.tsx b/orchestrator/src/client/main.tsx
new file mode 100644
index 0000000..f8136ef
--- /dev/null
+++ b/orchestrator/src/client/main.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import { App } from './App';
+import './styles/index.css';
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+);
diff --git a/orchestrator/src/client/styles/index.css b/orchestrator/src/client/styles/index.css
new file mode 100644
index 0000000..3d4ab42
--- /dev/null
+++ b/orchestrator/src/client/styles/index.css
@@ -0,0 +1,680 @@
+/* ===================================================================
+ Job Ops Orchestrator - Design System
+ A modern, dark-mode first design with glassmorphism and gradients
+ =================================================================== */
+
+/* CSS Custom Properties (Design Tokens) */
+:root {
+ /* Colors */
+ --color-background: #0a0a0f;
+ --color-surface: #12121a;
+ --color-surface-elevated: #1a1a25;
+ --color-surface-glass: rgba(26, 26, 37, 0.7);
+
+ --color-border: rgba(255, 255, 255, 0.08);
+ --color-border-light: rgba(255, 255, 255, 0.12);
+
+ --color-text-primary: #f5f5f7;
+ --color-text-secondary: #a1a1aa;
+ --color-text-muted: #71717a;
+
+ /* Accent colors */
+ --color-primary: #6366f1;
+ --color-primary-light: #818cf8;
+ --color-primary-dark: #4f46e5;
+ --color-primary-glow: rgba(99, 102, 241, 0.3);
+
+ --color-success: #10b981;
+ --color-success-light: #34d399;
+ --color-success-glow: rgba(16, 185, 129, 0.2);
+
+ --color-warning: #f59e0b;
+ --color-warning-light: #fbbf24;
+
+ --color-danger: #ef4444;
+ --color-danger-light: #f87171;
+ --color-danger-glow: rgba(239, 68, 68, 0.2);
+
+ --color-info: #3b82f6;
+ --color-info-light: #60a5fa;
+
+ /* Gradients */
+ --gradient-primary: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
+ --gradient-success: linear-gradient(135deg, #10b981 0%, #34d399 100%);
+ --gradient-mesh: radial-gradient(at 40% 20%, hsla(250, 80%, 60%, 0.1) 0px, transparent 50%),
+ radial-gradient(at 80% 0%, hsla(280, 80%, 50%, 0.1) 0px, transparent 50%),
+ radial-gradient(at 0% 50%, hsla(220, 100%, 60%, 0.05) 0px, transparent 50%);
+
+ /* Typography */
+ --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ --font-mono: 'SF Mono', Monaco, 'Cascadia Code', monospace;
+
+ /* Spacing */
+ --space-1: 0.25rem;
+ --space-2: 0.5rem;
+ --space-3: 0.75rem;
+ --space-4: 1rem;
+ --space-5: 1.25rem;
+ --space-6: 1.5rem;
+ --space-8: 2rem;
+ --space-10: 2.5rem;
+ --space-12: 3rem;
+
+ /* Border Radius */
+ --radius-sm: 0.375rem;
+ --radius-md: 0.5rem;
+ --radius-lg: 0.75rem;
+ --radius-xl: 1rem;
+ --radius-2xl: 1.5rem;
+ --radius-full: 9999px;
+
+ /* Shadows */
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -2px rgba(0, 0, 0, 0.3);
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -4px rgba(0, 0, 0, 0.4);
+ --shadow-glow: 0 0 20px var(--color-primary-glow);
+
+ /* Transitions */
+ --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-normal: 200ms cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+/* Reset & Base */
+*, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ font-size: 16px;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ font-family: var(--font-sans);
+ background-color: var(--color-background);
+ color: var(--color-text-primary);
+ line-height: 1.6;
+ min-height: 100vh;
+}
+
+#root {
+ min-height: 100vh;
+ background-image: var(--gradient-mesh);
+ background-attachment: fixed;
+}
+
+/* Typography */
+h1, h2, h3, h4, h5, h6 {
+ font-weight: 600;
+ line-height: 1.3;
+ letter-spacing: -0.02em;
+}
+
+h1 { font-size: 2rem; }
+h2 { font-size: 1.5rem; }
+h3 { font-size: 1.25rem; }
+h4 { font-size: 1.125rem; }
+
+a {
+ color: var(--color-primary-light);
+ text-decoration: none;
+ transition: color var(--transition-fast);
+}
+
+a:hover {
+ color: var(--color-primary);
+}
+
+/* Utility Classes */
+.container {
+ max-width: 1400px;
+ margin: 0 auto;
+ padding: 0 var(--space-6);
+}
+
+.flex { display: flex; }
+.flex-col { flex-direction: column; }
+.items-center { align-items: center; }
+.justify-between { justify-content: space-between; }
+.gap-2 { gap: var(--space-2); }
+.gap-3 { gap: var(--space-3); }
+.gap-4 { gap: var(--space-4); }
+.gap-6 { gap: var(--space-6); }
+
+/* Glass Card */
+.card {
+ background: var(--color-surface-glass);
+ backdrop-filter: blur(12px);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-xl);
+ padding: var(--space-6);
+ transition: all var(--transition-normal);
+}
+
+.card:hover {
+ border-color: var(--color-border-light);
+ box-shadow: var(--shadow-lg);
+}
+
+.card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: var(--space-4);
+}
+
+/* Buttons */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ padding: var(--space-3) var(--space-5);
+ font-family: var(--font-sans);
+ font-size: 0.875rem;
+ font-weight: 500;
+ border: none;
+ border-radius: var(--radius-lg);
+ cursor: pointer;
+ transition: all var(--transition-fast);
+ white-space: nowrap;
+}
+
+.btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.btn-primary {
+ background: var(--gradient-primary);
+ color: white;
+ box-shadow: var(--shadow-sm), 0 0 20px var(--color-primary-glow);
+}
+
+.btn-primary:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow: var(--shadow-md), 0 0 30px var(--color-primary-glow);
+}
+
+.btn-success {
+ background: var(--gradient-success);
+ color: white;
+}
+
+.btn-success:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow: var(--shadow-md), 0 0 20px var(--color-success-glow);
+}
+
+.btn-danger {
+ background: var(--color-danger);
+ color: white;
+}
+
+.btn-danger:hover:not(:disabled) {
+ background: var(--color-danger-light);
+}
+
+.btn-ghost {
+ background: transparent;
+ color: var(--color-text-secondary);
+ border: 1px solid var(--color-border);
+}
+
+.btn-ghost:hover:not(:disabled) {
+ background: var(--color-surface);
+ color: var(--color-text-primary);
+ border-color: var(--color-border-light);
+}
+
+.btn-icon {
+ padding: var(--space-2);
+ border-radius: var(--radius-md);
+}
+
+/* Status Badges */
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-1);
+ padding: var(--space-1) var(--space-3);
+ font-size: 0.75rem;
+ font-weight: 500;
+ border-radius: var(--radius-full);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+}
+
+.badge-discovered {
+ background: rgba(59, 130, 246, 0.15);
+ color: var(--color-info-light);
+ border: 1px solid rgba(59, 130, 246, 0.3);
+}
+
+.badge-processing {
+ background: rgba(245, 158, 11, 0.15);
+ color: var(--color-warning-light);
+ border: 1px solid rgba(245, 158, 11, 0.3);
+}
+
+.badge-ready {
+ background: rgba(16, 185, 129, 0.15);
+ color: var(--color-success-light);
+ border: 1px solid rgba(16, 185, 129, 0.3);
+}
+
+.badge-applied {
+ background: rgba(99, 102, 241, 0.15);
+ color: var(--color-primary-light);
+ border: 1px solid rgba(99, 102, 241, 0.3);
+}
+
+.badge-rejected {
+ background: rgba(239, 68, 68, 0.15);
+ color: var(--color-danger-light);
+ border: 1px solid rgba(239, 68, 68, 0.3);
+}
+
+.badge-expired {
+ background: rgba(113, 113, 122, 0.15);
+ color: var(--color-text-muted);
+ border: 1px solid rgba(113, 113, 122, 0.3);
+}
+
+/* Score indicator */
+.score {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-weight: 600;
+ font-size: 0.875rem;
+}
+
+.score-bar {
+ width: 60px;
+ height: 6px;
+ background: var(--color-surface);
+ border-radius: var(--radius-full);
+ overflow: hidden;
+}
+
+.score-bar-fill {
+ height: 100%;
+ border-radius: var(--radius-full);
+ transition: width var(--transition-slow);
+}
+
+.score-high .score-bar-fill { background: var(--color-success); }
+.score-medium .score-bar-fill { background: var(--color-warning); }
+.score-low .score-bar-fill { background: var(--color-danger); }
+
+/* Stats Grid */
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
+ gap: var(--space-4);
+}
+
+.stat-card {
+ background: var(--color-surface);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
+ padding: var(--space-4);
+ text-align: center;
+}
+
+.stat-value {
+ font-size: 2rem;
+ font-weight: 700;
+ background: var(--gradient-primary);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.stat-label {
+ font-size: 0.75rem;
+ color: var(--color-text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin-top: var(--space-1);
+}
+
+/* Job List */
+.job-list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-4);
+}
+
+.job-card {
+ background: var(--color-surface-glass);
+ backdrop-filter: blur(12px);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-xl);
+ padding: var(--space-5);
+ transition: all var(--transition-normal);
+}
+
+.job-card:hover {
+ border-color: var(--color-primary);
+ box-shadow: var(--shadow-glow);
+ transform: translateY(-2px);
+}
+
+.job-card-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: var(--space-4);
+ margin-bottom: var(--space-3);
+}
+
+.job-title {
+ font-size: 1.125rem;
+ font-weight: 600;
+ color: var(--color-text-primary);
+ margin-bottom: var(--space-1);
+}
+
+.job-employer {
+ font-size: 0.875rem;
+ color: var(--color-text-secondary);
+}
+
+.job-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-4);
+ margin-top: var(--space-3);
+ padding-top: var(--space-3);
+ border-top: 1px solid var(--color-border);
+}
+
+.job-meta-item {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-size: 0.8125rem;
+ color: var(--color-text-muted);
+}
+
+.job-meta-item svg {
+ width: 14px;
+ height: 14px;
+ opacity: 0.7;
+}
+
+.job-actions {
+ display: flex;
+ gap: var(--space-2);
+ margin-top: var(--space-4);
+}
+
+/* Tabs */
+.tabs {
+ display: flex;
+ gap: var(--space-1);
+ background: var(--color-surface);
+ padding: var(--space-1);
+ border-radius: var(--radius-lg);
+ margin-bottom: var(--space-6);
+}
+
+.tab {
+ flex: 1;
+ padding: var(--space-3) var(--space-4);
+ font-size: 0.875rem;
+ font-weight: 500;
+ color: var(--color-text-muted);
+ background: transparent;
+ border: none;
+ border-radius: var(--radius-md);
+ cursor: pointer;
+ transition: all var(--transition-fast);
+}
+
+.tab:hover {
+ color: var(--color-text-secondary);
+}
+
+.tab.active {
+ background: var(--gradient-primary);
+ color: white;
+}
+
+/* Empty State */
+.empty-state {
+ text-align: center;
+ padding: var(--space-12);
+ color: var(--color-text-muted);
+}
+
+.empty-state-icon {
+ font-size: 3rem;
+ margin-bottom: var(--space-4);
+ opacity: 0.5;
+}
+
+.empty-state-title {
+ font-size: 1.125rem;
+ font-weight: 600;
+ color: var(--color-text-secondary);
+ margin-bottom: var(--space-2);
+}
+
+/* Loading Spinner */
+.spinner {
+ width: 20px;
+ height: 20px;
+ border: 2px solid var(--color-border);
+ border-top-color: var(--color-primary);
+ border-radius: 50%;
+ animation: spin 0.8s linear infinite;
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+/* Pulse animation for processing jobs */
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.5; }
+}
+
+.pulse {
+ animation: pulse 2s ease-in-out infinite;
+}
+
+/* Header */
+.header {
+ padding: var(--space-6) 0;
+ border-bottom: 1px solid var(--color-border);
+ margin-bottom: var(--space-8);
+}
+
+.header-content {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.logo {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+}
+
+.logo-icon {
+ width: 40px;
+ height: 40px;
+ border-radius: var(--radius-lg);
+ background: var(--gradient-primary);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1.25rem;
+}
+
+.logo-text {
+ font-size: 1.25rem;
+ font-weight: 700;
+}
+
+/* Modal */
+.modal-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.7);
+ backdrop-filter: blur(4px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 100;
+ opacity: 0;
+ animation: fadeIn var(--transition-fast) forwards;
+}
+
+@keyframes fadeIn {
+ to { opacity: 1; }
+}
+
+.modal {
+ background: var(--color-surface);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-2xl);
+ max-width: 600px;
+ width: 90%;
+ max-height: 80vh;
+ overflow-y: auto;
+ animation: slideUp var(--transition-normal) forwards;
+}
+
+@keyframes slideUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.modal-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--space-5) var(--space-6);
+ border-bottom: 1px solid var(--color-border);
+}
+
+.modal-title {
+ font-size: 1.125rem;
+ font-weight: 600;
+}
+
+.modal-body {
+ padding: var(--space-6);
+}
+
+.modal-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: var(--space-3);
+ padding: var(--space-4) var(--space-6);
+ border-top: 1px solid var(--color-border);
+ background: var(--color-surface-elevated);
+ border-radius: 0 0 var(--radius-2xl) var(--radius-2xl);
+}
+
+/* Toast */
+.toast-container {
+ position: fixed;
+ bottom: var(--space-6);
+ right: var(--space-6);
+ z-index: 200;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+}
+
+.toast {
+ background: var(--color-surface-elevated);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
+ padding: var(--space-4) var(--space-5);
+ box-shadow: var(--shadow-lg);
+ min-width: 280px;
+ animation: slideIn var(--transition-normal) forwards;
+}
+
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: translateX(100%);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+.toast-success { border-left: 3px solid var(--color-success); }
+.toast-error { border-left: 3px solid var(--color-danger); }
+.toast-info { border-left: 3px solid var(--color-info); }
+
+/* Scrollbar */
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: var(--color-surface);
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--color-border-light);
+ border-radius: var(--radius-full);
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: var(--color-text-muted);
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .container {
+ padding: 0 var(--space-4);
+ }
+
+ h1 { font-size: 1.5rem; }
+
+ .header-content {
+ flex-direction: column;
+ gap: var(--space-4);
+ align-items: flex-start;
+ }
+
+ .stats-grid {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ .job-card-header {
+ flex-direction: column;
+ }
+
+ .job-actions {
+ flex-direction: column;
+ }
+
+ .job-actions .btn {
+ width: 100%;
+ }
+}
diff --git a/orchestrator/src/server/api/index.ts b/orchestrator/src/server/api/index.ts
new file mode 100644
index 0000000..11a6f1e
--- /dev/null
+++ b/orchestrator/src/server/api/index.ts
@@ -0,0 +1 @@
+export { apiRouter } from './routes.js';
diff --git a/orchestrator/src/server/api/routes.ts b/orchestrator/src/server/api/routes.ts
new file mode 100644
index 0000000..1c3f4f1
--- /dev/null
+++ b/orchestrator/src/server/api/routes.ts
@@ -0,0 +1,272 @@
+/**
+ * API routes for the orchestrator.
+ */
+
+import { Router, Request, Response } from 'express';
+import { z } from 'zod';
+import * as jobsRepo from '../repositories/jobs.js';
+import * as pipelineRepo from '../repositories/pipeline.js';
+import { runPipeline, processJob, getPipelineStatus } from '../pipeline/index.js';
+import { createNotionEntry } from '../services/notion.js';
+import type { JobStatus, ApiResponse, JobsListResponse, PipelineStatusResponse } from '../../shared/types.js';
+
+export const apiRouter = Router();
+
+// ============================================================================
+// Jobs API
+// ============================================================================
+
+/**
+ * GET /api/jobs - List all jobs
+ * Query params: status (comma-separated list of statuses to filter)
+ */
+apiRouter.get('/jobs', async (req: Request, res: Response) => {
+ try {
+ const statusFilter = req.query.status as string | undefined;
+ const statuses = statusFilter?.split(',').filter(Boolean) as JobStatus[] | undefined;
+
+ const jobs = await jobsRepo.getAllJobs(statuses);
+ const stats = await jobsRepo.getJobStats();
+
+ const response: ApiResponse = {
+ success: true,
+ data: {
+ jobs,
+ total: jobs.length,
+ byStatus: stats,
+ },
+ };
+
+ res.json(response);
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
+
+/**
+ * GET /api/jobs/:id - Get a single job
+ */
+apiRouter.get('/jobs/:id', async (req: Request, res: Response) => {
+ try {
+ const job = await jobsRepo.getJobById(req.params.id);
+
+ if (!job) {
+ return res.status(404).json({ success: false, error: 'Job not found' });
+ }
+
+ res.json({ success: true, data: job });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
+
+/**
+ * PATCH /api/jobs/:id - Update a job
+ */
+const updateJobSchema = z.object({
+ status: z.enum(['discovered', 'processing', 'ready', 'applied', 'rejected', 'expired']).optional(),
+ suitabilityScore: z.number().min(0).max(100).optional(),
+ suitabilityReason: z.string().optional(),
+ tailoredSummary: z.string().optional(),
+ pdfPath: z.string().optional(),
+});
+
+apiRouter.patch('/jobs/:id', async (req: Request, res: Response) => {
+ try {
+ const input = updateJobSchema.parse(req.body);
+ const job = await jobsRepo.updateJob(req.params.id, input);
+
+ if (!job) {
+ return res.status(404).json({ success: false, error: 'Job not found' });
+ }
+
+ res.json({ success: true, data: job });
+ } catch (error) {
+ if (error instanceof z.ZodError) {
+ return res.status(400).json({ success: false, error: error.message });
+ }
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
+
+/**
+ * POST /api/jobs/:id/process - Process a single job (generate summary + PDF)
+ */
+apiRouter.post('/jobs/:id/process', async (req: Request, res: Response) => {
+ try {
+ const result = await processJob(req.params.id);
+
+ if (!result.success) {
+ return res.status(400).json({ success: false, error: result.error });
+ }
+
+ const job = await jobsRepo.getJobById(req.params.id);
+ res.json({ success: true, data: job });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
+
+/**
+ * POST /api/jobs/:id/apply - Mark a job as applied and sync to Notion
+ */
+apiRouter.post('/jobs/:id/apply', async (req: Request, res: Response) => {
+ try {
+ const job = await jobsRepo.getJobById(req.params.id);
+
+ if (!job) {
+ return res.status(404).json({ success: false, error: 'Job not found' });
+ }
+
+ const appliedAt = new Date().toISOString();
+
+ // Sync to Notion
+ const notionResult = await createNotionEntry({
+ id: job.id,
+ title: job.title,
+ employer: job.employer,
+ applicationLink: job.applicationLink,
+ deadline: job.deadline,
+ salary: job.salary,
+ location: job.location,
+ pdfPath: job.pdfPath,
+ appliedAt,
+ });
+
+ // Update job status
+ const updatedJob = await jobsRepo.updateJob(job.id, {
+ status: 'applied',
+ appliedAt,
+ notionPageId: notionResult.pageId,
+ });
+
+ res.json({ success: true, data: updatedJob });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
+
+/**
+ * POST /api/jobs/:id/reject - Mark a job as rejected
+ */
+apiRouter.post('/jobs/:id/reject', async (req: Request, res: Response) => {
+ try {
+ const job = await jobsRepo.updateJob(req.params.id, { status: 'rejected' });
+
+ if (!job) {
+ return res.status(404).json({ success: false, error: 'Job not found' });
+ }
+
+ res.json({ success: true, data: job });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
+
+// ============================================================================
+// Pipeline API
+// ============================================================================
+
+/**
+ * GET /api/pipeline/status - Get pipeline status
+ */
+apiRouter.get('/pipeline/status', async (req: Request, res: Response) => {
+ try {
+ const { isRunning } = getPipelineStatus();
+ const lastRun = await pipelineRepo.getLatestPipelineRun();
+
+ const response: ApiResponse = {
+ success: true,
+ data: {
+ isRunning,
+ lastRun,
+ nextScheduledRun: null, // Would come from n8n
+ },
+ };
+
+ res.json(response);
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
+
+/**
+ * GET /api/pipeline/runs - Get recent pipeline runs
+ */
+apiRouter.get('/pipeline/runs', async (req: Request, res: Response) => {
+ try {
+ const runs = await pipelineRepo.getRecentPipelineRuns(20);
+ res.json({ success: true, data: runs });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
+
+/**
+ * POST /api/pipeline/run - Trigger the pipeline manually
+ */
+const runPipelineSchema = z.object({
+ topN: z.number().min(1).max(50).optional(),
+ minSuitabilityScore: z.number().min(0).max(100).optional(),
+});
+
+apiRouter.post('/pipeline/run', async (req: Request, res: Response) => {
+ try {
+ const config = runPipelineSchema.parse(req.body);
+
+ // Start pipeline in background
+ runPipeline(config).catch(console.error);
+
+ res.json({
+ success: true,
+ data: { message: 'Pipeline started' }
+ });
+ } catch (error) {
+ if (error instanceof z.ZodError) {
+ return res.status(400).json({ success: false, error: error.message });
+ }
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
+
+// ============================================================================
+// Webhook for n8n
+// ============================================================================
+
+/**
+ * POST /api/webhook/trigger - Webhook endpoint for n8n to trigger the pipeline
+ */
+apiRouter.post('/webhook/trigger', async (req: Request, res: Response) => {
+ // Optional: Add authentication check
+ const authHeader = req.headers.authorization;
+ const expectedToken = process.env.WEBHOOK_SECRET;
+
+ if (expectedToken && authHeader !== `Bearer ${expectedToken}`) {
+ return res.status(401).json({ success: false, error: 'Unauthorized' });
+ }
+
+ try {
+ // Start pipeline in background
+ runPipeline().catch(console.error);
+
+ res.json({
+ success: true,
+ data: {
+ message: 'Pipeline triggered',
+ triggeredAt: new Date().toISOString(),
+ }
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ res.status(500).json({ success: false, error: message });
+ }
+});
diff --git a/orchestrator/src/server/db/index.ts b/orchestrator/src/server/db/index.ts
new file mode 100644
index 0000000..24fcab9
--- /dev/null
+++ b/orchestrator/src/server/db/index.ts
@@ -0,0 +1,30 @@
+/**
+ * Database connection and initialization.
+ */
+
+import Database from 'better-sqlite3';
+import { drizzle } from 'drizzle-orm/better-sqlite3';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { existsSync, mkdirSync } from 'fs';
+import * as schema from './schema.js';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const DB_PATH = join(__dirname, '../../../data/jobs.db');
+
+// Ensure data directory exists
+const dataDir = dirname(DB_PATH);
+if (!existsSync(dataDir)) {
+ mkdirSync(dataDir, { recursive: true });
+}
+
+const sqlite = new Database(DB_PATH);
+sqlite.pragma('journal_mode = WAL');
+
+export const db = drizzle(sqlite, { schema });
+
+export { schema };
+
+export function closeDb() {
+ sqlite.close();
+}
diff --git a/orchestrator/src/server/db/migrate.ts b/orchestrator/src/server/db/migrate.ts
new file mode 100644
index 0000000..5487f22
--- /dev/null
+++ b/orchestrator/src/server/db/migrate.ts
@@ -0,0 +1,77 @@
+/**
+ * Database migration script - creates tables if they don't exist.
+ */
+
+import Database from 'better-sqlite3';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { existsSync, mkdirSync } from 'fs';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const DB_PATH = join(__dirname, '../../../data/jobs.db');
+
+// Ensure data directory exists
+const dataDir = dirname(DB_PATH);
+if (!existsSync(dataDir)) {
+ mkdirSync(dataDir, { recursive: true });
+}
+
+const sqlite = new Database(DB_PATH);
+
+const migrations = [
+ `CREATE TABLE IF NOT EXISTS jobs (
+ id TEXT PRIMARY KEY,
+ title TEXT NOT NULL,
+ employer TEXT NOT NULL,
+ employer_url TEXT,
+ job_url TEXT NOT NULL UNIQUE,
+ application_link TEXT,
+ disciplines TEXT,
+ deadline TEXT,
+ salary TEXT,
+ location TEXT,
+ degree_required TEXT,
+ starting TEXT,
+ job_description TEXT,
+ status TEXT NOT NULL DEFAULT 'discovered' CHECK(status IN ('discovered', 'processing', 'ready', 'applied', 'rejected', 'expired')),
+ suitability_score REAL,
+ suitability_reason TEXT,
+ tailored_summary TEXT,
+ pdf_path TEXT,
+ notion_page_id TEXT,
+ discovered_at TEXT NOT NULL DEFAULT (datetime('now')),
+ processed_at TEXT,
+ applied_at TEXT,
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
+ )`,
+
+ `CREATE TABLE IF NOT EXISTS pipeline_runs (
+ id TEXT PRIMARY KEY,
+ started_at TEXT NOT NULL DEFAULT (datetime('now')),
+ completed_at TEXT,
+ status TEXT NOT NULL DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed')),
+ jobs_discovered INTEGER NOT NULL DEFAULT 0,
+ jobs_processed INTEGER NOT NULL DEFAULT 0,
+ error_message TEXT
+ )`,
+
+ `CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status)`,
+ `CREATE INDEX IF NOT EXISTS idx_jobs_discovered_at ON jobs(discovered_at)`,
+ `CREATE INDEX IF NOT EXISTS idx_pipeline_runs_started_at ON pipeline_runs(started_at)`,
+];
+
+console.log('🔧 Running database migrations...');
+
+for (const migration of migrations) {
+ try {
+ sqlite.exec(migration);
+ console.log('✅ Migration applied');
+ } catch (error) {
+ console.error('❌ Migration failed:', error);
+ process.exit(1);
+ }
+}
+
+sqlite.close();
+console.log('🎉 Database migrations complete!');
diff --git a/orchestrator/src/server/db/schema.ts b/orchestrator/src/server/db/schema.ts
new file mode 100644
index 0000000..42784a8
--- /dev/null
+++ b/orchestrator/src/server/db/schema.ts
@@ -0,0 +1,58 @@
+/**
+ * Database schema using Drizzle ORM with SQLite.
+ */
+
+import { sqliteTable, text, integer, real } from 'drizzle-orm/sqlite-core';
+import { sql } from 'drizzle-orm';
+
+export const jobs = sqliteTable('jobs', {
+ id: text('id').primaryKey(),
+
+ // From crawler
+ title: text('title').notNull(),
+ employer: text('employer').notNull(),
+ employerUrl: text('employer_url'),
+ jobUrl: text('job_url').notNull().unique(),
+ applicationLink: text('application_link'),
+ disciplines: text('disciplines'),
+ deadline: text('deadline'),
+ salary: text('salary'),
+ location: text('location'),
+ degreeRequired: text('degree_required'),
+ starting: text('starting'),
+ jobDescription: text('job_description'),
+
+ // Orchestrator enrichments
+ status: text('status', {
+ enum: ['discovered', 'processing', 'ready', 'applied', 'rejected', 'expired']
+ }).notNull().default('discovered'),
+ suitabilityScore: real('suitability_score'),
+ suitabilityReason: text('suitability_reason'),
+ tailoredSummary: text('tailored_summary'),
+ pdfPath: text('pdf_path'),
+ notionPageId: text('notion_page_id'),
+
+ // Timestamps
+ discoveredAt: text('discovered_at').notNull().default(sql`(datetime('now'))`),
+ processedAt: text('processed_at'),
+ appliedAt: text('applied_at'),
+ createdAt: text('created_at').notNull().default(sql`(datetime('now'))`),
+ updatedAt: text('updated_at').notNull().default(sql`(datetime('now'))`),
+});
+
+export const pipelineRuns = sqliteTable('pipeline_runs', {
+ id: text('id').primaryKey(),
+ startedAt: text('started_at').notNull().default(sql`(datetime('now'))`),
+ completedAt: text('completed_at'),
+ status: text('status', {
+ enum: ['running', 'completed', 'failed']
+ }).notNull().default('running'),
+ jobsDiscovered: integer('jobs_discovered').notNull().default(0),
+ jobsProcessed: integer('jobs_processed').notNull().default(0),
+ errorMessage: text('error_message'),
+});
+
+export type JobRow = typeof jobs.$inferSelect;
+export type NewJobRow = typeof jobs.$inferInsert;
+export type PipelineRunRow = typeof pipelineRuns.$inferSelect;
+export type NewPipelineRunRow = typeof pipelineRuns.$inferInsert;
diff --git a/orchestrator/src/server/index.ts b/orchestrator/src/server/index.ts
new file mode 100644
index 0000000..a564c41
--- /dev/null
+++ b/orchestrator/src/server/index.ts
@@ -0,0 +1,71 @@
+/**
+ * Express server entry point.
+ */
+
+import express from 'express';
+import cors from 'cors';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { config } from 'dotenv';
+import { apiRouter } from './api/index.js';
+
+// Load environment variables from orchestrator root
+const __dirname = dirname(fileURLToPath(import.meta.url));
+config({ path: join(__dirname, '../../.env') });
+
+const app = express();
+const PORT = process.env.PORT || 3001;
+
+// Middleware
+app.use(cors());
+app.use(express.json());
+
+// Logging middleware
+app.use((req, res, next) => {
+ const start = Date.now();
+ res.on('finish', () => {
+ const duration = Date.now() - start;
+ console.log(`${req.method} ${req.path} - ${res.statusCode} (${duration}ms)`);
+ });
+ next();
+});
+
+// API routes
+app.use('/api', apiRouter);
+
+// Serve static files for generated PDFs
+const pdfDir = join(__dirname, '../../data/pdfs');
+app.use('/pdfs', express.static(pdfDir));
+
+// Health check
+app.get('/health', (req, res) => {
+ res.json({ status: 'ok', timestamp: new Date().toISOString() });
+});
+
+// Serve client app in production
+if (process.env.NODE_ENV === 'production') {
+ const clientDir = join(__dirname, '../../dist/client');
+ app.use(express.static(clientDir));
+
+ // SPA fallback
+ app.get('*', (req, res) => {
+ res.sendFile(join(clientDir, 'index.html'));
+ });
+}
+
+// Start server
+app.listen(PORT, () => {
+ console.log(`
+╔═══════════════════════════════════════════════════════════╗
+║ ║
+║ 🚀 Job Ops Orchestrator ║
+║ ║
+║ Server running at: http://localhost:${PORT} ║
+║ ║
+║ API: http://localhost:${PORT}/api ║
+║ Health: http://localhost:${PORT}/health ║
+║ PDFs: http://localhost:${PORT}/pdfs ║
+║ ║
+╚═══════════════════════════════════════════════════════════╝
+ `);
+});
diff --git a/orchestrator/src/server/pipeline/index.ts b/orchestrator/src/server/pipeline/index.ts
new file mode 100644
index 0000000..47060d4
--- /dev/null
+++ b/orchestrator/src/server/pipeline/index.ts
@@ -0,0 +1 @@
+export * from './orchestrator.js';
diff --git a/orchestrator/src/server/pipeline/orchestrator.ts b/orchestrator/src/server/pipeline/orchestrator.ts
new file mode 100644
index 0000000..dd2b3e7
--- /dev/null
+++ b/orchestrator/src/server/pipeline/orchestrator.ts
@@ -0,0 +1,283 @@
+/**
+ * Main pipeline logic - orchestrates the daily job processing flow.
+ *
+ * Flow:
+ * 1. Run crawler to discover new jobs
+ * 2. Score jobs for suitability
+ * 3. Pick top N jobs
+ * 4. Generate tailored summaries
+ * 5. Generate PDF resumes
+ * 6. Mark as "ready" for user review
+ */
+
+import { readFile } from 'fs/promises';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { runCrawler } from '../services/crawler.js';
+import { scoreAndRankJobs } from '../services/scorer.js';
+import { generateSummary } from '../services/summary.js';
+import { generatePdf } from '../services/pdf.js';
+import * as jobsRepo from '../repositories/jobs.js';
+import * as pipelineRepo from '../repositories/pipeline.js';
+import type { Job, PipelineConfig } from '../../shared/types.js';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const DEFAULT_PROFILE_PATH = join(__dirname, '../../../../resume-generator/base.json');
+
+const DEFAULT_CONFIG: PipelineConfig = {
+ topN: 10,
+ minSuitabilityScore: 50,
+ sources: ['gradcracker'],
+ profilePath: DEFAULT_PROFILE_PATH,
+ outputDir: join(__dirname, '../../../data/pdfs'),
+};
+
+// Track if pipeline is currently running
+let isPipelineRunning = false;
+
+/**
+ * Run the full job discovery and processing pipeline.
+ */
+export async function runPipeline(config: Partial = {}): Promise<{
+ success: boolean;
+ jobsDiscovered: number;
+ jobsProcessed: number;
+ error?: string;
+}> {
+ if (isPipelineRunning) {
+ return {
+ success: false,
+ jobsDiscovered: 0,
+ jobsProcessed: 0,
+ error: 'Pipeline is already running',
+ };
+ }
+
+ isPipelineRunning = true;
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
+
+ // Create pipeline run record
+ const pipelineRun = await pipelineRepo.createPipelineRun();
+
+ console.log('🚀 Starting job pipeline...');
+ console.log(` Config: topN=${mergedConfig.topN}, minScore=${mergedConfig.minSuitabilityScore}`);
+
+ try {
+ // Step 1: Load profile
+ console.log('\n📋 Loading profile...');
+ const profile = await loadProfile(mergedConfig.profilePath);
+
+ // Step 2: Run crawler
+ console.log('\n🕷️ Running crawler...');
+ const crawlerResult = await runCrawler();
+
+ if (!crawlerResult.success) {
+ throw new Error(`Crawler failed: ${crawlerResult.error}`);
+ }
+
+ // Step 3: Import discovered jobs
+ console.log('\n💾 Importing jobs to database...');
+ const { created, skipped } = await jobsRepo.bulkCreateJobs(crawlerResult.jobs);
+ console.log(` Created: ${created}, Skipped (duplicates): ${skipped}`);
+
+ await pipelineRepo.updatePipelineRun(pipelineRun.id, {
+ jobsDiscovered: created,
+ });
+
+ // Step 4: Get unprocessed jobs and score them
+ console.log('\n🎯 Scoring jobs for suitability...');
+ const unprocessedJobs = await jobsRepo.getJobsForProcessing(50); // Get more than topN for ranking
+ const rankedJobs = await scoreAndRankJobs(unprocessedJobs, profile);
+
+ // Update scores in database
+ for (const job of rankedJobs) {
+ await jobsRepo.updateJob(job.id, {
+ suitabilityScore: job.suitabilityScore,
+ suitabilityReason: job.suitabilityReason,
+ });
+ }
+
+ // Step 5: Pick top N jobs above threshold
+ const topJobs = rankedJobs
+ .filter(j => j.suitabilityScore >= mergedConfig.minSuitabilityScore)
+ .slice(0, mergedConfig.topN);
+
+ console.log(`\n📊 Selected ${topJobs.length} top jobs for processing:`);
+ for (const job of topJobs) {
+ console.log(` - ${job.title} @ ${job.employer} (score: ${job.suitabilityScore})`);
+ }
+
+ // Step 6: Process each top job
+ let processed = 0;
+
+ for (const job of topJobs) {
+ console.log(`\n📝 Processing: ${job.title} @ ${job.employer}`);
+
+ try {
+ // Mark as processing
+ await jobsRepo.updateJob(job.id, { status: 'processing' });
+
+ // Generate tailored summary
+ console.log(' Generating summary...');
+ const summaryResult = await generateSummary(
+ job.jobDescription || '',
+ profile
+ );
+
+ if (!summaryResult.success) {
+ console.warn(` ⚠️ Summary generation failed: ${summaryResult.error}`);
+ continue;
+ }
+
+ // Update job with summary
+ await jobsRepo.updateJob(job.id, {
+ tailoredSummary: summaryResult.summary,
+ });
+
+ // Generate PDF
+ console.log(' Generating PDF...');
+ const pdfResult = await generatePdf(
+ job.id,
+ summaryResult.summary!,
+ mergedConfig.profilePath
+ );
+
+ if (!pdfResult.success) {
+ console.warn(` ⚠️ PDF generation failed: ${pdfResult.error}`);
+ // Still mark as ready even if PDF failed - user can regenerate
+ }
+
+ // Mark as ready
+ await jobsRepo.updateJob(job.id, {
+ status: 'ready',
+ pdfPath: pdfResult.pdfPath ?? null,
+ });
+
+ processed++;
+ console.log(` ✅ Ready for review!`);
+
+ } catch (error) {
+ console.error(` ❌ Failed to process job: ${error}`);
+ // Continue with next job
+ }
+ }
+
+ // Update pipeline run as completed
+ await pipelineRepo.updatePipelineRun(pipelineRun.id, {
+ status: 'completed',
+ completedAt: new Date().toISOString(),
+ jobsProcessed: processed,
+ });
+
+ console.log('\n🎉 Pipeline completed!');
+ console.log(` Jobs discovered: ${created}`);
+ console.log(` Jobs processed: ${processed}`);
+
+ isPipelineRunning = false;
+
+ return {
+ success: true,
+ jobsDiscovered: created,
+ jobsProcessed: processed,
+ };
+
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+
+ await pipelineRepo.updatePipelineRun(pipelineRun.id, {
+ status: 'failed',
+ completedAt: new Date().toISOString(),
+ errorMessage: message,
+ });
+
+ isPipelineRunning = false;
+
+ console.error('\n❌ Pipeline failed:', message);
+
+ return {
+ success: false,
+ jobsDiscovered: 0,
+ jobsProcessed: 0,
+ error: message,
+ };
+ }
+}
+
+/**
+ * Process a single job (for manual processing).
+ */
+export async function processJob(jobId: string): Promise<{
+ success: boolean;
+ error?: string;
+}> {
+ console.log(`📝 Processing job ${jobId}...`);
+
+ try {
+ const job = await jobsRepo.getJobById(jobId);
+ if (!job) {
+ return { success: false, error: 'Job not found' };
+ }
+
+ const profile = await loadProfile(DEFAULT_PROFILE_PATH);
+
+ // Mark as processing
+ await jobsRepo.updateJob(job.id, { status: 'processing' });
+
+ // Generate summary if not already done
+ if (!job.tailoredSummary) {
+ console.log(' Generating summary...');
+ const summaryResult = await generateSummary(
+ job.jobDescription || '',
+ profile
+ );
+
+ if (summaryResult.success) {
+ await jobsRepo.updateJob(job.id, {
+ tailoredSummary: summaryResult.summary,
+ });
+ job.tailoredSummary = summaryResult.summary ?? null;
+ }
+ }
+
+ // Generate PDF
+ console.log(' Generating PDF...');
+ const pdfResult = await generatePdf(
+ job.id,
+ job.tailoredSummary || '',
+ DEFAULT_PROFILE_PATH
+ );
+
+ // Mark as ready
+ await jobsRepo.updateJob(job.id, {
+ status: 'ready',
+ pdfPath: pdfResult.pdfPath ?? null,
+ });
+
+ console.log(' ✅ Done!');
+ return { success: true };
+
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ return { success: false, error: message };
+ }
+}
+
+/**
+ * Check if pipeline is currently running.
+ */
+export function getPipelineStatus(): { isRunning: boolean } {
+ return { isRunning: isPipelineRunning };
+}
+
+/**
+ * Load the user profile from JSON file.
+ */
+async function loadProfile(profilePath: string): Promise> {
+ try {
+ const content = await readFile(profilePath, 'utf-8');
+ return JSON.parse(content);
+ } catch (error) {
+ console.warn('Failed to load profile, using empty object');
+ return {};
+ }
+}
diff --git a/orchestrator/src/server/pipeline/run.ts b/orchestrator/src/server/pipeline/run.ts
new file mode 100644
index 0000000..b8e1e1a
--- /dev/null
+++ b/orchestrator/src/server/pipeline/run.ts
@@ -0,0 +1,45 @@
+/**
+ * Standalone script to run the pipeline.
+ * Can be triggered by n8n or cron.
+ *
+ * Usage: npm run pipeline:run
+ */
+
+import { config } from 'dotenv';
+import { runPipeline } from './orchestrator.js';
+import { closeDb } from '../db/index.js';
+
+// Load environment variables
+config();
+
+async function main() {
+ console.log('='.repeat(60));
+ console.log('🚀 Job Pipeline Runner');
+ console.log(` Started at: ${new Date().toISOString()}`);
+ console.log('='.repeat(60));
+
+ const result = await runPipeline({
+ topN: parseInt(process.env.PIPELINE_TOP_N || '10'),
+ minSuitabilityScore: parseInt(process.env.PIPELINE_MIN_SCORE || '50'),
+ });
+
+ console.log('\n' + '='.repeat(60));
+ console.log('📊 Pipeline Results:');
+ console.log(` Success: ${result.success}`);
+ console.log(` Jobs Discovered: ${result.jobsDiscovered}`);
+ console.log(` Jobs Processed: ${result.jobsProcessed}`);
+ if (result.error) {
+ console.log(` Error: ${result.error}`);
+ }
+ console.log(` Completed at: ${new Date().toISOString()}`);
+ console.log('='.repeat(60));
+
+ closeDb();
+ process.exit(result.success ? 0 : 1);
+}
+
+main().catch((error) => {
+ console.error('Fatal error:', error);
+ closeDb();
+ process.exit(1);
+});
diff --git a/orchestrator/src/server/repositories/index.ts b/orchestrator/src/server/repositories/index.ts
new file mode 100644
index 0000000..25a0848
--- /dev/null
+++ b/orchestrator/src/server/repositories/index.ts
@@ -0,0 +1,2 @@
+export * from './jobs.js';
+export * from './pipeline.js';
diff --git a/orchestrator/src/server/repositories/jobs.ts b/orchestrator/src/server/repositories/jobs.ts
new file mode 100644
index 0000000..a280893
--- /dev/null
+++ b/orchestrator/src/server/repositories/jobs.ts
@@ -0,0 +1,190 @@
+/**
+ * Job repository - data access layer for jobs.
+ */
+
+import { eq, desc, sql, and, inArray } from 'drizzle-orm';
+import { randomUUID } from 'crypto';
+import { db, schema } from '../db/index.js';
+import type { Job, CreateJobInput, UpdateJobInput, JobStatus } from '../../shared/types.js';
+
+const { jobs } = schema;
+
+/**
+ * Get all jobs, optionally filtered by status.
+ */
+export async function getAllJobs(statuses?: JobStatus[]): Promise {
+ const query = statuses && statuses.length > 0
+ ? db.select().from(jobs).where(inArray(jobs.status, statuses)).orderBy(desc(jobs.discoveredAt))
+ : db.select().from(jobs).orderBy(desc(jobs.discoveredAt));
+
+ const rows = await query;
+ return rows.map(mapRowToJob);
+}
+
+/**
+ * Get a single job by ID.
+ */
+export async function getJobById(id: string): Promise {
+ const [row] = await db.select().from(jobs).where(eq(jobs.id, id));
+ return row ? mapRowToJob(row) : null;
+}
+
+/**
+ * Get a job by its URL (for deduplication).
+ */
+export async function getJobByUrl(jobUrl: string): Promise {
+ const [row] = await db.select().from(jobs).where(eq(jobs.jobUrl, jobUrl));
+ return row ? mapRowToJob(row) : null;
+}
+
+/**
+ * Create a new job (or return existing if URL matches).
+ */
+export async function createJob(input: CreateJobInput): Promise {
+ // Check for existing job with same URL
+ const existing = await getJobByUrl(input.jobUrl);
+ if (existing) {
+ return existing;
+ }
+
+ const id = randomUUID();
+ const now = new Date().toISOString();
+
+ await db.insert(jobs).values({
+ id,
+ title: input.title,
+ employer: input.employer,
+ employerUrl: input.employerUrl ?? null,
+ jobUrl: input.jobUrl,
+ applicationLink: input.applicationLink ?? null,
+ disciplines: input.disciplines ?? null,
+ deadline: input.deadline ?? null,
+ salary: input.salary ?? null,
+ location: input.location ?? null,
+ degreeRequired: input.degreeRequired ?? null,
+ starting: input.starting ?? null,
+ jobDescription: input.jobDescription ?? null,
+ status: 'discovered',
+ discoveredAt: now,
+ createdAt: now,
+ updatedAt: now,
+ });
+
+ return (await getJobById(id))!;
+}
+
+/**
+ * Update a job.
+ */
+export async function updateJob(id: string, input: UpdateJobInput): Promise {
+ const now = new Date().toISOString();
+
+ await db.update(jobs)
+ .set({
+ ...input,
+ updatedAt: now,
+ ...(input.status === 'processing' ? { processedAt: now } : {}),
+ ...(input.status === 'applied' && !input.appliedAt ? { appliedAt: now } : {}),
+ })
+ .where(eq(jobs.id, id));
+
+ return getJobById(id);
+}
+
+/**
+ * Bulk create jobs from crawler results.
+ */
+export async function bulkCreateJobs(inputs: CreateJobInput[]): Promise<{ created: number; skipped: number }> {
+ let created = 0;
+ let skipped = 0;
+
+ for (const input of inputs) {
+ const existing = await getJobByUrl(input.jobUrl);
+ if (existing) {
+ skipped++;
+ continue;
+ }
+
+ await createJob(input);
+ created++;
+ }
+
+ return { created, skipped };
+}
+
+/**
+ * Get job statistics by status.
+ */
+export async function getJobStats(): Promise> {
+ const result = await db
+ .select({
+ status: jobs.status,
+ count: sql`count(*)`,
+ })
+ .from(jobs)
+ .groupBy(jobs.status);
+
+ const stats: Record = {
+ discovered: 0,
+ processing: 0,
+ ready: 0,
+ applied: 0,
+ rejected: 0,
+ expired: 0,
+ };
+
+ for (const row of result) {
+ stats[row.status as JobStatus] = row.count;
+ }
+
+ return stats;
+}
+
+/**
+ * Get jobs ready for processing (discovered with description).
+ */
+export async function getJobsForProcessing(limit: number = 10): Promise {
+ const rows = await db
+ .select()
+ .from(jobs)
+ .where(
+ and(
+ eq(jobs.status, 'discovered'),
+ sql`${jobs.jobDescription} IS NOT NULL`
+ )
+ )
+ .orderBy(desc(jobs.discoveredAt))
+ .limit(limit);
+
+ return rows.map(mapRowToJob);
+}
+
+// Helper to map database row to Job type
+function mapRowToJob(row: typeof jobs.$inferSelect): Job {
+ return {
+ id: row.id,
+ title: row.title,
+ employer: row.employer,
+ employerUrl: row.employerUrl,
+ jobUrl: row.jobUrl,
+ applicationLink: row.applicationLink,
+ disciplines: row.disciplines,
+ deadline: row.deadline,
+ salary: row.salary,
+ location: row.location,
+ degreeRequired: row.degreeRequired,
+ starting: row.starting,
+ jobDescription: row.jobDescription,
+ status: row.status as JobStatus,
+ suitabilityScore: row.suitabilityScore,
+ suitabilityReason: row.suitabilityReason,
+ tailoredSummary: row.tailoredSummary,
+ pdfPath: row.pdfPath,
+ notionPageId: row.notionPageId,
+ discoveredAt: row.discoveredAt,
+ processedAt: row.processedAt,
+ appliedAt: row.appliedAt,
+ createdAt: row.createdAt,
+ updatedAt: row.updatedAt,
+ };
+}
diff --git a/orchestrator/src/server/repositories/pipeline.ts b/orchestrator/src/server/repositories/pipeline.ts
new file mode 100644
index 0000000..2c30be5
--- /dev/null
+++ b/orchestrator/src/server/repositories/pipeline.ts
@@ -0,0 +1,94 @@
+/**
+ * Pipeline run repository.
+ */
+
+import { eq, desc } from 'drizzle-orm';
+import { randomUUID } from 'crypto';
+import { db, schema } from '../db/index.js';
+import type { PipelineRun } from '../../shared/types.js';
+
+const { pipelineRuns } = schema;
+
+/**
+ * Create a new pipeline run.
+ */
+export async function createPipelineRun(): Promise {
+ const id = randomUUID();
+ const now = new Date().toISOString();
+
+ await db.insert(pipelineRuns).values({
+ id,
+ startedAt: now,
+ status: 'running',
+ });
+
+ return {
+ id,
+ startedAt: now,
+ completedAt: null,
+ status: 'running',
+ jobsDiscovered: 0,
+ jobsProcessed: 0,
+ errorMessage: null,
+ };
+}
+
+/**
+ * Update a pipeline run.
+ */
+export async function updatePipelineRun(
+ id: string,
+ update: Partial<{
+ completedAt: string;
+ status: 'running' | 'completed' | 'failed';
+ jobsDiscovered: number;
+ jobsProcessed: number;
+ errorMessage: string;
+ }>
+): Promise {
+ await db.update(pipelineRuns).set(update).where(eq(pipelineRuns.id, id));
+}
+
+/**
+ * Get the latest pipeline run.
+ */
+export async function getLatestPipelineRun(): Promise {
+ const [row] = await db
+ .select()
+ .from(pipelineRuns)
+ .orderBy(desc(pipelineRuns.startedAt))
+ .limit(1);
+
+ if (!row) return null;
+
+ return {
+ id: row.id,
+ startedAt: row.startedAt,
+ completedAt: row.completedAt,
+ status: row.status as PipelineRun['status'],
+ jobsDiscovered: row.jobsDiscovered,
+ jobsProcessed: row.jobsProcessed,
+ errorMessage: row.errorMessage,
+ };
+}
+
+/**
+ * Get recent pipeline runs.
+ */
+export async function getRecentPipelineRuns(limit: number = 10): Promise {
+ const rows = await db
+ .select()
+ .from(pipelineRuns)
+ .orderBy(desc(pipelineRuns.startedAt))
+ .limit(limit);
+
+ return rows.map(row => ({
+ id: row.id,
+ startedAt: row.startedAt,
+ completedAt: row.completedAt,
+ status: row.status as PipelineRun['status'],
+ jobsDiscovered: row.jobsDiscovered,
+ jobsProcessed: row.jobsProcessed,
+ errorMessage: row.errorMessage,
+ }));
+}
diff --git a/orchestrator/src/server/services/crawler.ts b/orchestrator/src/server/services/crawler.ts
new file mode 100644
index 0000000..7de78ac
--- /dev/null
+++ b/orchestrator/src/server/services/crawler.ts
@@ -0,0 +1,112 @@
+/**
+ * Service for running the job crawler (job-extractor).
+ * Wraps the existing Crawlee-based crawler.
+ */
+
+import { spawn } from 'child_process';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { readdir, readFile } from 'fs/promises';
+import type { CreateJobInput } from '../../shared/types.js';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const CRAWLER_DIR = join(__dirname, '../../../../job-extractor');
+const STORAGE_DIR = join(CRAWLER_DIR, 'storage/datasets/default');
+
+export interface CrawlerResult {
+ success: boolean;
+ jobs: CreateJobInput[];
+ error?: string;
+}
+
+/**
+ * Run the job-extractor crawler and return discovered jobs.
+ */
+export async function runCrawler(): Promise {
+ console.log('🕷️ Starting job crawler...');
+
+ try {
+ // Clear previous results
+ await clearStorageDataset();
+
+ // Run the crawler
+ await new Promise((resolve, reject) => {
+ const child = spawn('npm', ['run', 'start'], {
+ cwd: CRAWLER_DIR,
+ shell: true,
+ stdio: 'inherit',
+ });
+
+ child.on('close', (code) => {
+ if (code === 0) {
+ resolve();
+ } else {
+ reject(new Error(`Crawler exited with code ${code}`));
+ }
+ });
+
+ child.on('error', reject);
+ });
+
+ // Read crawled jobs from storage
+ const jobs = await readCrawledJobs();
+
+ console.log(`✅ Crawler completed. Found ${jobs.length} jobs.`);
+
+ return { success: true, jobs };
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ console.error('❌ Crawler failed:', message);
+ return { success: false, jobs: [], error: message };
+ }
+}
+
+/**
+ * Read crawled jobs from the Crawlee storage dataset.
+ */
+async function readCrawledJobs(): Promise {
+ try {
+ const files = await readdir(STORAGE_DIR);
+ const jsonFiles = files.filter(f => f.endsWith('.json'));
+
+ const jobs: CreateJobInput[] = [];
+
+ for (const file of jsonFiles) {
+ const content = await readFile(join(STORAGE_DIR, file), 'utf-8');
+ const data = JSON.parse(content);
+
+ // Map crawler output to our job input format
+ jobs.push({
+ title: data.title || 'Unknown Title',
+ employer: data.employer || 'Unknown Employer',
+ employerUrl: data.employerUrl,
+ jobUrl: data.url || data.jobUrl,
+ applicationLink: data.applicationLink,
+ disciplines: data.disciplines,
+ deadline: data.deadline,
+ salary: data.salary,
+ location: data.location,
+ degreeRequired: data.degreeRequired,
+ starting: data.starting,
+ jobDescription: data.jobDescription,
+ });
+ }
+
+ return jobs;
+ } catch (error) {
+ console.error('Failed to read crawled jobs:', error);
+ return [];
+ }
+}
+
+/**
+ * Clear previous crawl results.
+ */
+async function clearStorageDataset(): Promise {
+ const { rm } = await import('fs/promises');
+ try {
+ await rm(STORAGE_DIR, { recursive: true, force: true });
+ } catch {
+ // Ignore if directory doesn't exist
+ }
+}
diff --git a/orchestrator/src/server/services/index.ts b/orchestrator/src/server/services/index.ts
new file mode 100644
index 0000000..43a7e1f
--- /dev/null
+++ b/orchestrator/src/server/services/index.ts
@@ -0,0 +1,5 @@
+export * from './crawler.js';
+export * from './scorer.js';
+export * from './summary.js';
+export * from './pdf.js';
+export * from './notion.js';
diff --git a/orchestrator/src/server/services/notion.ts b/orchestrator/src/server/services/notion.ts
new file mode 100644
index 0000000..5a9bf5b
--- /dev/null
+++ b/orchestrator/src/server/services/notion.ts
@@ -0,0 +1,89 @@
+/**
+ * Service for syncing with Notion.
+ */
+
+export interface NotionSyncResult {
+ success: boolean;
+ pageId?: string;
+ error?: string;
+}
+
+/**
+ * Create a job entry in Notion.
+ *
+ * This is a placeholder - implement based on your Notion setup.
+ */
+export async function createNotionEntry(job: {
+ id: string;
+ title: string;
+ employer: string;
+ applicationLink: string | null;
+ deadline: string | null;
+ salary: string | null;
+ location: string | null;
+ pdfPath: string | null;
+ appliedAt: string;
+}): Promise {
+ const notionApiKey = process.env.NOTION_API_KEY;
+ const notionDatabaseId = process.env.NOTION_DATABASE_ID;
+
+ if (!notionApiKey || !notionDatabaseId) {
+ console.log('ℹ️ Notion API not configured, skipping sync');
+ return { success: true, pageId: undefined };
+ }
+
+ try {
+ const response = await fetch('https://api.notion.com/v1/pages', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${notionApiKey}`,
+ 'Content-Type': 'application/json',
+ 'Notion-Version': '2022-06-28',
+ },
+ body: JSON.stringify({
+ parent: { database_id: notionDatabaseId },
+ properties: {
+ // Customize these based on your Notion database schema
+ 'Name': {
+ title: [{ text: { content: `${job.title} @ ${job.employer}` } }],
+ },
+ 'Company': {
+ rich_text: [{ text: { content: job.employer } }],
+ },
+ 'Application Link': job.applicationLink ? {
+ url: job.applicationLink,
+ } : undefined,
+ 'Deadline': job.deadline ? {
+ date: { start: job.deadline },
+ } : undefined,
+ 'Salary': job.salary ? {
+ rich_text: [{ text: { content: job.salary } }],
+ } : undefined,
+ 'Location': job.location ? {
+ rich_text: [{ text: { content: job.location } }],
+ } : undefined,
+ 'Applied Date': {
+ date: { start: job.appliedAt },
+ },
+ 'Status': {
+ status: { name: 'Applied' },
+ },
+ },
+ }),
+ });
+
+ if (!response.ok) {
+ const error = await response.text();
+ throw new Error(`Notion API error: ${response.status} - ${error}`);
+ }
+
+ const data = await response.json();
+
+ console.log(`✅ Created Notion entry: ${data.id}`);
+ return { success: true, pageId: data.id };
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ console.error(`❌ Notion sync failed: ${message}`);
+ return { success: false, error: message };
+ }
+}
diff --git a/orchestrator/src/server/services/pdf.ts b/orchestrator/src/server/services/pdf.ts
new file mode 100644
index 0000000..5df4a36
--- /dev/null
+++ b/orchestrator/src/server/services/pdf.ts
@@ -0,0 +1,144 @@
+/**
+ * Service for generating PDF resumes using RXResume.
+ * Wraps the existing Python rxresume_automation.py script.
+ */
+
+import { spawn } from 'child_process';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { readFile, writeFile, copyFile, access, mkdir } from 'fs/promises';
+import { existsSync } from 'fs';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const RESUME_GEN_DIR = join(__dirname, '../../../../resume-generator');
+const OUTPUT_DIR = join(__dirname, '../../../data/pdfs');
+
+export interface PdfResult {
+ success: boolean;
+ pdfPath?: string;
+ error?: string;
+}
+
+/**
+ * Generate a tailored PDF resume for a job.
+ *
+ * @param jobId - Unique job identifier (used for filename)
+ * @param tailoredSummary - The AI-generated summary to inject
+ * @param baseResumePath - Path to the base resume JSON (optional)
+ */
+export async function generatePdf(
+ jobId: string,
+ tailoredSummary: string,
+ baseResumePath?: string
+): Promise {
+ console.log(`📄 Generating PDF for job ${jobId}...`);
+
+ const resumeJsonPath = baseResumePath || join(RESUME_GEN_DIR, 'base.json');
+
+ try {
+ // Ensure output directory exists
+ if (!existsSync(OUTPUT_DIR)) {
+ await mkdir(OUTPUT_DIR, { recursive: true });
+ }
+
+ // Read base resume
+ const baseResume = JSON.parse(await readFile(resumeJsonPath, 'utf-8'));
+
+ // Inject tailored summary
+ if (baseResume.sections?.summary) {
+ baseResume.sections.summary.content = tailoredSummary;
+ } else if (baseResume.basics?.summary) {
+ baseResume.basics.summary = tailoredSummary;
+ }
+
+ // Write modified resume to temp file
+ const tempResumePath = join(RESUME_GEN_DIR, `temp_resume_${jobId}.json`);
+ await writeFile(tempResumePath, JSON.stringify(baseResume, null, 2));
+
+ // Generate PDF using Python script
+ const outputFilename = `resume_${jobId}.pdf`;
+ const outputPath = join(OUTPUT_DIR, outputFilename);
+
+ await runPythonPdfGenerator(tempResumePath, outputFilename);
+
+ // Move generated PDF to our output directory
+ const pythonOutputPath = join(RESUME_GEN_DIR, 'resumes', outputFilename);
+
+ try {
+ await access(pythonOutputPath);
+ await copyFile(pythonOutputPath, outputPath);
+ } catch {
+ // PDF might already be in the right place or script output different location
+ console.warn('PDF not found at expected Python output location');
+ }
+
+ // Cleanup temp file
+ try {
+ const { unlink } = await import('fs/promises');
+ await unlink(tempResumePath);
+ } catch {
+ // Ignore cleanup errors
+ }
+
+ console.log(`✅ PDF generated: ${outputPath}`);
+ return { success: true, pdfPath: outputPath };
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ console.error(`❌ PDF generation failed: ${message}`);
+ return { success: false, error: message };
+ }
+}
+
+/**
+ * Run the Python RXResume automation script.
+ */
+async function runPythonPdfGenerator(
+ jsonPath: string,
+ outputFilename: string
+): Promise {
+ return new Promise((resolve, reject) => {
+ // Note: This calls the Python script with the JSON path
+ // The Python script needs to be modified to accept these args
+ // For now, we'll use environment variables
+
+ const child = spawn('python3', ['rxresume_automation.py'], {
+ cwd: RESUME_GEN_DIR,
+ env: {
+ ...process.env,
+ RESUME_JSON_PATH: jsonPath,
+ OUTPUT_FILENAME: outputFilename,
+ },
+ stdio: 'inherit',
+ });
+
+ child.on('close', (code) => {
+ if (code === 0) {
+ resolve();
+ } else {
+ reject(new Error(`Python script exited with code ${code}`));
+ }
+ });
+
+ child.on('error', reject);
+ });
+}
+
+/**
+ * Check if a PDF exists for a job.
+ */
+export async function pdfExists(jobId: string): Promise {
+ const pdfPath = join(OUTPUT_DIR, `resume_${jobId}.pdf`);
+ try {
+ await access(pdfPath);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+/**
+ * Get the path to a job's PDF.
+ */
+export function getPdfPath(jobId: string): string {
+ return join(OUTPUT_DIR, `resume_${jobId}.pdf`);
+}
diff --git a/orchestrator/src/server/services/scorer.ts b/orchestrator/src/server/services/scorer.ts
new file mode 100644
index 0000000..814739e
--- /dev/null
+++ b/orchestrator/src/server/services/scorer.ts
@@ -0,0 +1,143 @@
+/**
+ * Service for scoring job suitability using AI.
+ */
+
+import type { Job } from '../../shared/types.js';
+
+const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
+
+interface SuitabilityResult {
+ score: number; // 0-100
+ reason: string; // Explanation
+}
+
+/**
+ * Score a job's suitability based on profile and job description.
+ */
+export async function scoreJobSuitability(
+ job: Job,
+ profile: Record
+): Promise {
+ const apiKey = process.env.OPENROUTER_API_KEY;
+ if (!apiKey) {
+ console.warn('⚠️ OPENROUTER_API_KEY not set, using mock scoring');
+ return mockScore(job);
+ }
+
+ const model = process.env.MODEL || 'openai/gpt-4o-mini';
+
+ const prompt = buildScoringPrompt(job, profile);
+
+ try {
+ const response = await fetch(OPENROUTER_API_URL, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${apiKey}`,
+ 'Content-Type': 'application/json',
+ 'HTTP-Referer': 'http://localhost',
+ 'X-Title': 'JobOpsOrchestrator',
+ },
+ body: JSON.stringify({
+ model,
+ messages: [{ role: 'user', content: prompt }],
+ response_format: { type: 'json_object' },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`OpenRouter error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ const content = data.choices[0]?.message?.content;
+
+ if (!content) {
+ throw new Error('No content in response');
+ }
+
+ const parsed = JSON.parse(content);
+ return {
+ score: Math.min(100, Math.max(0, parsed.score || 0)),
+ reason: parsed.reason || 'No explanation provided',
+ };
+ } catch (error) {
+ console.error('Failed to score job:', error);
+ return mockScore(job);
+ }
+}
+
+function buildScoringPrompt(job: Job, profile: Record): string {
+ return `
+You are evaluating a job listing for a candidate. Score how suitable this job is for the candidate on a scale of 0-100.
+
+Consider:
+- Skills match (technologies, frameworks, languages)
+- Experience level match
+- Location/remote work alignment
+- Industry/domain fit
+- Career growth potential
+
+Candidate Profile:
+${JSON.stringify(profile, null, 2)}
+
+Job Listing:
+Title: ${job.title}
+Employer: ${job.employer}
+Location: ${job.location || 'Not specified'}
+Salary: ${job.salary || 'Not specified'}
+Degree Required: ${job.degreeRequired || 'Not specified'}
+Disciplines: ${job.disciplines || 'Not specified'}
+
+Job Description:
+${job.jobDescription || 'No description available'}
+
+Respond with JSON: { "score": <0-100>, "reason": "" }
+`;
+}
+
+function mockScore(job: Job): SuitabilityResult {
+ // Simple keyword-based scoring as fallback
+ const jd = (job.jobDescription || '').toLowerCase();
+ const title = job.title.toLowerCase();
+
+ const goodKeywords = ['typescript', 'react', 'node', 'python', 'web', 'frontend', 'backend', 'fullstack', 'software', 'engineer', 'developer'];
+ const badKeywords = ['senior', '5+ years', '10+ years', 'principal', 'staff', 'manager'];
+
+ let score = 50;
+
+ for (const kw of goodKeywords) {
+ if (jd.includes(kw) || title.includes(kw)) score += 5;
+ }
+
+ for (const kw of badKeywords) {
+ if (jd.includes(kw) || title.includes(kw)) score -= 10;
+ }
+
+ score = Math.min(100, Math.max(0, score));
+
+ return {
+ score,
+ reason: 'Scored using keyword matching (API key not configured)',
+ };
+}
+
+/**
+ * Score multiple jobs and return sorted by score (descending).
+ */
+export async function scoreAndRankJobs(
+ jobs: Job[],
+ profile: Record
+): Promise> {
+ const scoredJobs = await Promise.all(
+ jobs.map(async (job) => {
+ const { score, reason } = await scoreJobSuitability(job, profile);
+ return {
+ ...job,
+ suitabilityScore: score,
+ suitabilityReason: reason,
+ };
+ })
+ );
+
+ return scoredJobs.sort((a, b) => b.suitabilityScore - a.suitabilityScore);
+}
diff --git a/orchestrator/src/server/services/summary.ts b/orchestrator/src/server/services/summary.ts
new file mode 100644
index 0000000..3ef98df
--- /dev/null
+++ b/orchestrator/src/server/services/summary.ts
@@ -0,0 +1,153 @@
+/**
+ * Service for generating tailored resume summaries.
+ * Wraps the existing Python generate_summary.py script.
+ */
+
+import { spawn } from 'child_process';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { writeFile, unlink } from 'fs/promises';
+import { randomUUID } from 'crypto';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const RESUME_GEN_DIR = join(__dirname, '../../../../resume-generator');
+
+const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
+
+export interface SummaryResult {
+ success: boolean;
+ summary?: string;
+ error?: string;
+}
+
+/**
+ * Generate a tailored resume summary for a job.
+ * Uses the native implementation instead of calling Python.
+ */
+export async function generateSummary(
+ jobDescription: string,
+ profile: Record
+): Promise {
+ const apiKey = process.env.OPENROUTER_API_KEY;
+
+ if (!apiKey) {
+ console.warn('⚠️ OPENROUTER_API_KEY not set, cannot generate summary');
+ return { success: false, error: 'API key not configured' };
+ }
+
+ const model = process.env.MODEL || 'openai/gpt-4o-mini';
+
+ const prompt = buildSummaryPrompt(profile, jobDescription);
+
+ try {
+ const response = await fetch(OPENROUTER_API_URL, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${apiKey}`,
+ 'Content-Type': 'application/json',
+ 'HTTP-Referer': 'http://localhost',
+ 'X-Title': 'JobOpsOrchestrator',
+ },
+ body: JSON.stringify({
+ model,
+ messages: [{ role: 'user', content: prompt }],
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`OpenRouter error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ const summary = data.choices[0]?.message?.content;
+
+ if (!summary) {
+ throw new Error('No content in response');
+ }
+
+ return { success: true, summary: summary.trim() };
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ return { success: false, error: message };
+ }
+}
+
+function buildSummaryPrompt(profile: Record, jd: string): string {
+ return `
+You are generating a tailored résumé summary for me.
+
+Requirements:
+- Use keywords found in the job description.
+- Keep it concise but meaningful. Avoid fluff. Avoid long-winded text.
+- Include just enough detail to feel real and grounded.
+- Gently convey that I care about helping people and doing good work.
+- Do NOT invent experience or skills I don't have.
+- Maintain a warm, confident, human tone.
+- Target THIS specific job directly, so use ATS keywords, while remaining natural.
+- Use the profile to add context and details.
+
+My profile (JSON fields merged):
+${JSON.stringify(profile, null, 2)}
+
+Job description:
+${jd}
+
+Write the résumé summary now.
+`;
+}
+
+/**
+ * Alternative: Call the Python script directly.
+ * Useful if the Python script has additional functionality.
+ */
+export async function generateSummaryViaPython(
+ jobDescription: string
+): Promise {
+ const tempFile = join(RESUME_GEN_DIR, `temp_jd_${randomUUID()}.txt`);
+
+ try {
+ // Write JD to temp file
+ await writeFile(tempFile, jobDescription);
+
+ // Call Python script
+ const result = await new Promise((resolve, reject) => {
+ let output = '';
+ let error = '';
+
+ const child = spawn('python3', ['generate_summary.py', '--file', tempFile], {
+ cwd: RESUME_GEN_DIR,
+ env: { ...process.env },
+ });
+
+ child.stdout.on('data', (data) => {
+ output += data.toString();
+ });
+
+ child.stderr.on('data', (data) => {
+ error += data.toString();
+ });
+
+ child.on('close', (code) => {
+ if (code === 0) {
+ resolve(output);
+ } else {
+ reject(new Error(error || `Process exited with code ${code}`));
+ }
+ });
+
+ child.on('error', reject);
+ });
+
+ return { success: true, summary: result.trim() };
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ return { success: false, error: message };
+ } finally {
+ // Cleanup temp file
+ try {
+ await unlink(tempFile);
+ } catch {
+ // Ignore cleanup errors
+ }
+ }
+}
diff --git a/orchestrator/src/shared/index.ts b/orchestrator/src/shared/index.ts
new file mode 100644
index 0000000..fcb073f
--- /dev/null
+++ b/orchestrator/src/shared/index.ts
@@ -0,0 +1 @@
+export * from './types';
diff --git a/orchestrator/src/shared/types.ts b/orchestrator/src/shared/types.ts
new file mode 100644
index 0000000..f6cd0a0
--- /dev/null
+++ b/orchestrator/src/shared/types.ts
@@ -0,0 +1,106 @@
+/**
+ * Shared types for the job-ops orchestrator.
+ */
+
+export type JobStatus =
+ | 'discovered' // Crawled but not processed
+ | 'processing' // Currently generating resume
+ | 'ready' // PDF generated, waiting for user to apply
+ | 'applied' // User marked as applied (added to Notion)
+ | 'rejected' // User rejected this job
+ | 'expired'; // Deadline passed
+
+export interface Job {
+ id: string;
+
+ // From crawler
+ title: string;
+ employer: string;
+ employerUrl: string | null;
+ jobUrl: string; // Gradcracker listing URL
+ applicationLink: string | null; // Actual application URL
+ disciplines: string | null;
+ deadline: string | null;
+ salary: string | null;
+ location: string | null;
+ degreeRequired: string | null;
+ starting: string | null;
+ jobDescription: string | null;
+
+ // Orchestrator enrichments
+ status: JobStatus;
+ suitabilityScore: number | null; // 0-100 AI-generated score
+ suitabilityReason: string | null; // AI explanation
+ tailoredSummary: string | null; // Generated resume summary
+ pdfPath: string | null; // Path to generated PDF
+ notionPageId: string | null; // Notion page ID if synced
+
+ // Timestamps
+ discoveredAt: string;
+ processedAt: string | null;
+ appliedAt: string | null;
+ createdAt: string;
+ updatedAt: string;
+}
+
+export interface CreateJobInput {
+ title: string;
+ employer: string;
+ employerUrl?: string;
+ jobUrl: string;
+ applicationLink?: string;
+ disciplines?: string;
+ deadline?: string;
+ salary?: string;
+ location?: string;
+ degreeRequired?: string;
+ starting?: string;
+ jobDescription?: string;
+}
+
+export interface UpdateJobInput {
+ status?: JobStatus;
+ suitabilityScore?: number;
+ suitabilityReason?: string;
+ tailoredSummary?: string;
+ pdfPath?: string;
+ notionPageId?: string;
+ appliedAt?: string;
+}
+
+export interface PipelineConfig {
+ topN: number; // Number of top jobs to process
+ minSuitabilityScore: number; // Minimum score to auto-process
+ sources: string[]; // Job sources to crawl
+ profilePath: string; // Path to profile JSON
+ outputDir: string; // Directory for generated PDFs
+}
+
+export interface PipelineRun {
+ id: string;
+ startedAt: string;
+ completedAt: string | null;
+ status: 'running' | 'completed' | 'failed';
+ jobsDiscovered: number;
+ jobsProcessed: number;
+ errorMessage: string | null;
+}
+
+// API Response types
+export interface ApiResponse {
+ success: boolean;
+ data?: T;
+ error?: string;
+}
+
+export interface JobsListResponse {
+ jobs: Job[];
+ total: number;
+ byStatus: Record;
+}
+
+export interface PipelineStatusResponse {
+ isRunning: boolean;
+ lastRun: PipelineRun | null;
+ nextScheduledRun: string | null;
+}
diff --git a/orchestrator/tsconfig.json b/orchestrator/tsconfig.json
new file mode 100644
index 0000000..2f94da5
--- /dev/null
+++ b/orchestrator/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "esModuleInterop": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"],
+ "@server/*": ["src/server/*"],
+ "@client/*": ["src/client/*"],
+ "@shared/*": ["src/shared/*"]
+ }
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/orchestrator/tsconfig.server.json b/orchestrator/tsconfig.server.json
new file mode 100644
index 0000000..0f600b8
--- /dev/null
+++ b/orchestrator/tsconfig.server.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "outDir": "./dist/server",
+ "rootDir": "./src/server"
+ },
+ "include": ["src/server/**/*", "src/shared/**/*"]
+}
diff --git a/orchestrator/vite.config.ts b/orchestrator/vite.config.ts
new file mode 100644
index 0000000..a7efc16
--- /dev/null
+++ b/orchestrator/vite.config.ts
@@ -0,0 +1,27 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import path from 'path';
+
+export default defineConfig({
+ plugins: [react()],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ '@client': path.resolve(__dirname, './src/client'),
+ '@shared': path.resolve(__dirname, './src/shared'),
+ },
+ },
+ server: {
+ port: 5173,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:3001',
+ changeOrigin: true,
+ },
+ },
+ },
+ build: {
+ outDir: 'dist/client',
+ emptyOutDir: true,
+ },
+});
diff --git a/resume-generator/rxresume_automation.py b/resume-generator/rxresume_automation.py
index 3b12560..6d52170 100644
--- a/resume-generator/rxresume_automation.py
+++ b/resume-generator/rxresume_automation.py
@@ -7,8 +7,8 @@ from pathlib import Path
from playwright.sync_api import sync_playwright
# Configuration
-RXRESUME_EMAIL = os.getenv("RXRESUME_EMAIL", "ssarfaraz@lancashire.ac.uk")
-RXRESUME_PASSWORD = os.getenv("RXRESUME_PASSWORD", "thisisatestpassword")
+RXRESUME_EMAIL = os.getenv("RXRESUME_EMAIL", "")
+RXRESUME_PASSWORD = os.getenv("RXRESUME_PASSWORD", "")
BASE_DIR = Path(__file__).parent
RESUME_JSON_PATH = BASE_DIR / "base.json"
@@ -79,7 +79,7 @@ def generate_resume_pdf(
output_path = OUTPUT_DIR / output_filename
with sync_playwright() as playwright:
- browser = playwright.chromium.launch(headless=False)
+ browser = playwright.chromium.launch(headless=True)
context = browser.new_context()
page = context.new_page()
diff --git a/resume-generator/temp_resume_b551b26e-7cf0-4bc5-be69-36d8e813f5b2.json b/resume-generator/temp_resume_b551b26e-7cf0-4bc5-be69-36d8e813f5b2.json
new file mode 100644
index 0000000..681171e
--- /dev/null
+++ b/resume-generator/temp_resume_b551b26e-7cf0-4bc5-be69-36d8e813f5b2.json
@@ -0,0 +1,661 @@
+{
+ "basics": {
+ "name": "Shaheer Sarfaraz",
+ "headline": "Frontend Software Engineer (React/TypeScript) · Autodesk Intern · Open Source & Product Work",
+ "email": "shaheer30sarfaraz@gmail.com",
+ "phone": "+44 7359 501592",
+ "location": "Blackpool, United Kingdom",
+ "url": {
+ "label": "https://dakheera47.com/",
+ "href": "https://dakheera47.com/"
+ },
+ "customFields": [],
+ "picture": {
+ "url": "",
+ "size": 120,
+ "aspectRatio": 1,
+ "borderRadius": 0,
+ "effects": {
+ "hidden": false,
+ "border": false,
+ "grayscale": false
+ }
+ }
+ },
+ "sections": {
+ "summary": {
+ "name": "Summary",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "summary",
+ "content": ""
+ },
+ "awards": {
+ "name": "Awards",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "awards",
+ "items": []
+ },
+ "certifications": {
+ "name": "Certifications",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "certifications",
+ "items": []
+ },
+ "education": {
+ "name": "Education",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "education",
+ "items": [
+ {
+ "id": "yo3p200zo45c6cdqc6a2vtt3",
+ "visible": true,
+ "institution": "University of Lancashire",
+ "studyType": "BSc (Hons) Computer Science",
+ "area": "Preston, United Kingdom",
+ "score": "1st Class",
+ "date": "September 2022 to June 2026",
+ "summary": "Relevant Modules: Web Applications, Algorithms & Data Structures, Game Development, Databases, Software Engineering (Agile group project)
",
+ "url": {
+ "label": "",
+ "href": "https://www.lancashire.ac.uk/undergraduate/courses/computer-science-bsc"
+ }
+ },
+ {
+ "id": "ei2fvjokusg3cfmdyolmgcoz",
+ "visible": false,
+ "institution": " ",
+ "studyType": "",
+ "area": "A Levels",
+ "score": "",
+ "date": "",
+ "summary": "Maths: A
Computer Science: B
Physics: C
Chemistry: E
",
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "pm4r5hngvv1w4mc79o22irfx",
+ "visible": false,
+ "institution": " ",
+ "studyType": "",
+ "area": "GCSEs",
+ "score": "",
+ "date": "",
+ "summary": "English: A*
Computer Science: A*
Urdu: A
Islamiat: A
Pakistan Studies: A
Biology: A
Chemistry: A
Physics: A
Maths: A
",
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ }
+ ]
+ },
+ "experience": {
+ "name": "Experience",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "experience",
+ "items": [
+ {
+ "id": "ng9ui2azk7w4y8oyu8kazqeb",
+ "visible": true,
+ "company": "Autodesk",
+ "position": "Software Engineering Intern",
+ "location": "Hybrid (Sheffield Based)",
+ "date": "July 2024 - June 2025",
+ "summary": "Implemented front-end features and fixes in the Autodesk Construction Cloud Model Coordination app, working in a ~10-year-old React/JavaScript/TypeScript codebase (7k+ commits) using Webpack module federation and Autodesk’s Exoskeleton dev environment
Improved reliability of the Cypress end-to-end test suite by diagnosing flaky tests, adding new E2E coverage, and participating in focused “test fest” events ahead of major feature releases
Collaborated with cross-functional teams (like the Design System, platform teams) by raising well-scoped bugs , augmenting existing tickets with reproduction steps and context, and aligning on shared component and API changes
Helped strengthen team processes by running weekly stand-ups and retrospectives, organising a ticket-scoping meeting, and participating in technical reviews & ADR discussions (e.g. standardising error handling and planning clash data streaming)
",
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "lhw25d7gf32wgdfpsktf6e0x",
+ "visible": true,
+ "company": "Mirage",
+ "position": "Co-Founder & Lead Developer",
+ "location": "",
+ "date": "December 2019 to Present",
+ "summary": "Delivered 10+ production websites and webapps for small and medium size clients (e.g. Indus Marine Services, Mumtaz Urdu), from initial scoping to deployment and handover
Built with modern web stacks (Next.js, Node/Express, Tailwind, Strapi, WordPress/Elementor where appropriate), setting up CI/CD and hosting
Led a small team of four developers , handling code reviews, task breakdown, and client communication
",
+ "url": {
+ "label": "",
+ "href": "https://promirage.com/"
+ }
+ },
+ {
+ "id": "k6zxqunkb225hbjso3c3vykk",
+ "visible": true,
+ "company": "University of Lancashire",
+ "position": "Computing Student Mentor",
+ "location": "Preston, UK",
+ "date": "July 2023 - July 2024",
+ "summary": "Academic Support and Leadership: Provided academic guidance to over 10 first-year students once a week, significantly enhancing their understanding and skills in key subjects like programming and web development.
Collaborative Learning Environment: Actively fostered a collaborative and supportive learning environment for a group of 10 students. This role also honed my leadership and communication skills, facilitating better academic outcomes for mentees.
",
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "a1bg5d8gp8sulf91xzdcsiaq",
+ "visible": true,
+ "company": "Research and Knowledge Exchange Institute",
+ "position": "Undergraduate Research Intern (HCI & EdTech)",
+ "location": "",
+ "date": "Summer 2024",
+ "summary": "Built a mouse “torch-reveal” web app (Astro ) to approximate eye-tracking; ran on-campus studies with Revoe Learning Academy pupils—1 eye-tracked, 9 using my app.
Logged cursor paths, dwell time, and reveal order; delivered setup notes for staff to run sessions independently.
Developed a Questionnaire Randomiser (Next.js): selectable response metrics (smileys / numbers / stars ), configurable randomisation strategies, and ZIP export of per-student PDFs ready for print.
Extras: lightweight analytics for comparison with the eye-tracking baseline; optional CSV/JSON data export.
",
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "tx32suzrg2bs5eumcbjei4ns",
+ "visible": false,
+ "company": "University of Lancashire",
+ "position": "Student Ambassador",
+ "location": "Preston, UK",
+ "date": "July 2023 - Present",
+ "summary": "Diverse Role Engagement: Actively engaged in various tasks, from guiding tours to assisting on open days, demonstrating adaptability and organizational skills.
Campus Culture Promotion: Contributed to enhancing the university’s inclusive campus atmosphere, showcasing the university's vibrant community to prospective students.
",
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ }
+ ]
+ },
+ "volunteer": {
+ "name": "Volunteering",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "volunteer",
+ "items": []
+ },
+ "interests": {
+ "name": "Interests",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": false,
+ "id": "interests",
+ "items": []
+ },
+ "languages": {
+ "name": "Languages",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "languages",
+ "items": []
+ },
+ "profiles": {
+ "name": "Profiles",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "profiles",
+ "items": [
+ {
+ "id": "ukl0uecvzkgm27mlye0wazlb",
+ "visible": true,
+ "network": "GitHub",
+ "username": "DaKheera47",
+ "icon": "github",
+ "url": {
+ "label": "",
+ "href": "https://github.com/DaKheera47"
+ }
+ },
+ {
+ "id": "cnbk5f0aeqvhx69ebk7hktwd",
+ "visible": true,
+ "network": "LinkedIn",
+ "username": "ssarfaraz30",
+ "icon": "linkedin",
+ "url": {
+ "label": "",
+ "href": "https://www.linkedin.com/in/ssarfaraz30/"
+ }
+ },
+ {
+ "id": "linnyxv78zdep1xwirpa2ia1",
+ "visible": true,
+ "network": "Hashnode",
+ "username": "DaKheera47",
+ "icon": "hashnode",
+ "url": {
+ "label": "",
+ "href": "https://dakheera47.hashnode.dev/"
+ }
+ }
+ ]
+ },
+ "projects": {
+ "name": "Projects",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "projects",
+ "items": [
+ {
+ "id": "yw843emozcth8s1ubi1ubvlf",
+ "visible": false,
+ "name": "Atoro",
+ "description": "Lead Developer",
+ "date": "January 2023",
+ "summary": "Next.js Implementation for Enhanced SEO: Utilized Next.js to optimize the website for search engines, significantly improving its online visibility and user engagement.
Strapi Backend Integration: Streamlined content management by implementing a Strapi backend, enhancing the efficiency and scalability of the website's content updates.
Responsive Design with Tailwind CSS: Employed Tailwind CSS for a utility-first approach, ensuring the website's responsiveness and seamless user experience across various devices.
Continuous Deployment Pipeline Establishment: Developed a continuous deployment pipeline, ensuring real-time updates and maintaining high performance and reliability of the website.
Optimized Web Performance: Focused on optimizing web performance by efficiently loading images and managing JavaScript bundles, leading to a faster and more efficient user experience.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://atoro.promirage.com"
+ }
+ },
+ {
+ "id": "ncxgdjjky54gh59iz2t1xi1v",
+ "visible": false,
+ "name": "Stellar Consultancy",
+ "description": "Lead Developer",
+ "date": "April 2023",
+ "summary": "WordPress and Elementor Integration: Expertly utilized WordPress with Elementor to build a robust content management system, enhancing the website's scalability and user interaction capabilities.
Client Engagement and Trust Building: Implemented features to showcase client testimonials, effectively building trust and displaying the success of previous project engagements.
Intuitive Design and User Engagement: Focused on intuitive page design and structuring, streamlining site maintenance and content updates, thereby enhancing user engagement.
Effective Call-to-Actions: Crafted clear call-to-actions and provided essential contact information, significantly improving user interaction and conversion rates.
Portfolio Display for Business Showcase: Presented past work and services offered through a comprehensive portfolio display, allowing visitors to assess the quality and impact of Stellar Consultancy's services.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://stellarconsultancy.ca"
+ }
+ },
+ {
+ "id": "tcecguinuctb8mu2xqrn97m8",
+ "visible": true,
+ "name": "Mumtaz Urdu",
+ "description": "Developer",
+ "date": "July 2022",
+ "summary": "Server-Rendered Web Application Development : Created the Mumtaz Urdu platform with Next.js to optimize server-side rendering for enhanced SEO and performance.
UI Development with Tailwind CSS : Implemented utility-first Tailwind CSS, ensuring rapid, responsive design for a seamless user interface.
Scalable Storage Solution : Integrated scalable Amazon S3 storage, supporting the application's growth and robust data management.
Progressive Web App Implementation : Developed PWA features for Mumtaz Urdu, offering users native-like mobile access and increased engagement.
High Traffic Data Management : Engineered Mumtaz Urdu's backend with Next.js and MongoDB, enabling the handling and efficient processing of vast amounts of user data for thousands of monthly users.
Test-Driven Development : Embraced TDD practices to ensure reliable and high-quality code, facilitating regular testing throughout the development process for continuous improvement.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://www.mumtazurdu.com/"
+ }
+ },
+ {
+ "id": "to47h749kaj6t02j3f9kprxq",
+ "visible": false,
+ "name": "PyScreeze",
+ "description": "Open Source Contribution",
+ "date": "January 2022",
+ "summary": "Innovative Feature Implementation: Implemented the locateCenterOnScreenNear function for PyScreeze, enhancing the library's functionality by enabling precise image location near a specified point on the screen.
Open Source Contribution: Marked my debut in open-source contributions with this significant addition to PyScreeze, showcasing my initiative and ability to contribute effectively to community-driven projects.
Collaborative Development and Recognition: Collaborated with the project's maintainer, asweigart, to refine and integrate the function into the main codebase, receiving recognition for this valuable contribution to the project.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://github.com/asweigart/pyscreeze/pull/79"
+ }
+ },
+ {
+ "id": "gt7yq82ulor5hmmutdhuvfo1",
+ "visible": false,
+ "name": "Threegency",
+ "description": "Lead Developer",
+ "date": "February 2023",
+ "summary": "Framework : Utilized Next.js to build a server-rendered React website, enhancing SEO and ensuring optimal performance.
Styling : Employed Tailwind CSS for utility-first styling, facilitating rapid UI development.
Content Management : Leveraged Strapi as a CMS, enabling streamlined content updates and administration.
Data Handling : Utilized GraphQL for data handling, ensuring efficient and flexible data retrieval.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://www.threegency.com"
+ }
+ },
+ {
+ "id": "c8fcu3nz541a4d5zcurx6b8c",
+ "visible": false,
+ "name": "AutoClass",
+ "description": "GUI Automation",
+ "date": "November 2021",
+ "summary": "Framework : Written in Python, leveraging the versatility and ease-of-use of the language.
Automation Library : Utilized PyAutoGUI for automating user interactions, enhancing the utility of the application.
Iterative Improvement : Progressively refined over a year, demonstrating a commitment to robustness and reliability.
Project Purpose : Developed to automate the process of joining Zoom classes, alleviating the repetitive morning routine.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://github.com/DaKheera47/autoclass"
+ }
+ },
+ {
+ "id": "rv23bgibq6bye6rujmcx1ygc",
+ "visible": false,
+ "name": "Meet Link Generator",
+ "description": "GUI Automation",
+ "date": "January 2022",
+ "summary": "Functionality : Generates Google Meet links with specific words in the URL by brute-forcing the creation of thousands of links until the desired pattern is achieved. Doing so enables creation of Google Meet links with specific codes or phrases.
Optimized Automation : The final product uses Python with PyAutoGUI for efficient and rapid creation of new Google Meet links.
Speed and Efficiency : Drastically improved performance, finally achieving the link generation time to under 1 second per link, limited only by internet speed.
Interface Interaction : Utilizes the Google Meet homepage's features for quicker link generation, avoiding full page refreshes for speed.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://github.com/DaKheera47/meet-link-generator"
+ }
+ },
+ {
+ "id": "tu98rghbi5c43ogget5mh7ih",
+ "visible": false,
+ "name": "UCLan Server-side Web Application Project",
+ "description": "",
+ "date": "UCLan Year 1",
+ "summary": "Backend Development with PHP and MySQL: Developed the backend for a Student’s Union Shop web application, integrating PHP and MySQL for dynamic data handling and backend database communication.
User Authentication and Session Management: Implemented user sign-up and login functionality using PHP sessions, enabling secure and personalized shopping experiences.
Dynamic Content Display from Database: Enhanced the application to dynamically display products and offers directly from the database, moving away from static HTML content.
Advanced Search and Personalization Features: Integrated advanced product search capabilities and personalized user greetings, improving user interactivity and engagement.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "ov4lkbc1vl169ynfnj91m1lm",
+ "visible": false,
+ "name": "Square About",
+ "description": "",
+ "date": "UCLan Year 1",
+ "summary": "Advanced 3D Game Development: Implemented a complex 3D game using TL-Engine, featuring intricate gameplay mechanics and immersive 3D visuals.
Dynamic Gameplay Elements: Integrated multiple spheres with varying behaviors, including super-spheres requiring multiple hits, enhancing the game's challenge and engagement levels.
Interactive Game Controls: Developed features for speed control and directional change, allowing players to interact dynamically with the game environment.
Strategic Game Mechanics: Added a bullet firing mechanism with a limited ammo concept, introducing strategic elements and a scoring system to the gameplay.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "s3r37gdr0oa84a6dp6r5nl58",
+ "visible": false,
+ "name": "Car Smash",
+ "description": "",
+ "date": "UCLan Year 1",
+ "summary": "3D Car Smash Game Development: Developed a 3D car smash game using TL-Engine, showcasing skills in game engine utilization and 3D gaming.
Collision Detection Mechanics: Implemented advanced collision detection between player's car and enemy vehicles, enhancing gameplay realism.
Dynamic Game States and Camera Views: Integrated multiple game states and camera views, including a chase camera and first-person view, for an immersive gaming experience.
Enhanced Player Interaction: Created a more realistic driving experience with accelerated movement and bounce effects on collisions, and introduced particle systems for visual effects.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "gylzkvl103m9s7ywag4xpdy4",
+ "visible": false,
+ "name": "Tweet Filter",
+ "description": "",
+ "date": "UCLan Year 1",
+ "summary": "Tweet Filtration System: Crafted a C++ program to filter out prohibited words from tweets, showcasing text processing and file handling capabilities.
Advanced Text Manipulation: Enhanced the program to filter varying cases and contexts of banned words, even within larger strings, demonstrating attention to detail in string operations.
Output Generation: Implemented functionality to write filtered tweets to new files, maintaining data integrity and displaying proficiency in file I/O operations.
Algorithm Optimization: Utilized data structures like vectors and implemented mathematical techniques for efficient word frequency analysis and sentiment determination.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "enav754zxhuc9uycbb83s94q",
+ "visible": false,
+ "name": "Burger Ordering App",
+ "description": "",
+ "date": "UCLan Year 1",
+ "summary": "Interactive Console Application: Engineered a C++ console application simulating a burger ordering process, highlighting proficiency in creating user-interactive software.
Complex Logic Implementation: Designed and implemented complex logic for burger size and topping selection, including pricing and order summary features.
Data Handling and User Input: Developed robust credit system and user input validation for an intuitive ordering experience, showcasing attention to detail and user-centric design.
Readable and Maintainable Code: Produced well-documented, maintainable code with clear variable naming and structured formatting, demonstrating best practices in software development.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "hl6jgeswr01tlul3iwoat05d",
+ "visible": false,
+ "name": "LinkLander",
+ "description": "Android Studio, Kotlin",
+ "date": "December 2023 - Ongoing",
+ "summary": "Innovative Android Utility: Developed LinkLander, a Kotlin-based Android application that simplifies the process of downloading online content directly to devices.
User-Centric Design: Focused on addressing Android system limitations by providing a seamless shortcut for redirecting links to an online video downloading service.
Simplicity and Efficiency: Emphasized a user-friendly interface, enhancing the Android experience by streamlining content downloads.
Technical Proficiency in Kotlin: Leveraged the capabilities of Kotlin for Android development to create a practical solution for niche digital tasks.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "v4s0ljbiiio198y8l1wl0ym6",
+ "visible": false,
+ "name": "AR App Development with AGILE",
+ "description": "Unity, C#",
+ "date": "October 2023 - Ongoing",
+ "summary": "Agile Development in Action : Participated in an Agile team project, developing an AR application for supporting disabled students with a team of five, demonstrating an application of Agile methodologies in a real-world scenario.
Mobile AR Application Prototype : Developed a proof-of-concept prototype using Unity and C# for mobile platforms, showcasing technical skills in modern app development environments.
Collaborative Software Engineering : Engaged in a collaborative environment, contributing code and ideas, emphasizing teamwork and shared responsibility in software creation.
Presentation and Critical Analysis : Delivered a comprehensive presentation and critical report, evaluating the Agile process, product development, and personal learning outcomes.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "fwxrq682hqrj1y76rmziqrbk",
+ "visible": true,
+ "name": "Indus Marine Services",
+ "description": "System Design & Development",
+ "date": "May 2022 - Ongoing",
+ "summary": "Induction System for Marine Services : Designed & developed an induction system for Indus Marine Services in the UAE, streamlining the employee onboarding process with interactive testing and certification issuance.
Admin-Centric Functionality : Devised a back-end system allowing admins to oversee inductee progress, manage documents, and curate customized quizzes as per requirements
Client Engagement Interface : Implemented a user-friendly front-end where inductees receive personalized email prompts, complete quizzes, and obtain certifications, all contributing to a seamless induction experience.
Robust Tech Stack Integration : Utilized a sophisticated stack comprising Node.js, Express, EJS, and Tailwind CSS to build a responsive, scalable, and easily navigable system.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "http://www.ims-auh.com"
+ }
+ },
+ {
+ "id": "jdfyaez8vq1b7xfr9rmxmz06",
+ "visible": false,
+ "name": "VECTOR AI",
+ "description": "Website Development",
+ "date": "February 2024 - February 2024",
+ "summary": "Innovative AI Development : As the driving force behind VECTOR's website development, I spearheaded the technical design using Astro, with a cutting-edge stack including React and Tailwind CSS.
Data-Driven Content Strategy : Leveraged Astro content management capabilities to structure and present data, ensuring content is dynamic, easily accessible, and optimized for both performance and scalability.
Astro for Enhanced Performance : Utilized Astro for static site generation, making VECTOR's website performance fast for a pleasant user experience
React for Responsive Interaction : Utilized React’s robust ecosystem to develop interactive elements, ensuring that each module of VECTOR’s platform is engaging and seamless for users across various touchpoints.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://vector-ai.co/"
+ }
+ },
+ {
+ "id": "qdhmfkqpfql19ohfas1g91ek",
+ "visible": false,
+ "name": "UCLan's First Hackathon",
+ "description": "Hackathon, Team Work",
+ "date": "February 2024",
+ "summary": "Second Place in UCLan Hackathon : Earned second place in UCLan's first hackathon by developing an app to simplify university life. Focused on enhancing the attendance monitoring process for student mentors.
TRPC for End-to-End Type Safety : Utilized TRPC to ensure end-to-end type safety, enhancing the app's reliability and streamlining the development process.
Supabase Backend Integration : Implemented Supabase as a backend solution, providing a robust and scalable database for managing attendance data efficiently.
Amazon SES and OAuth Integration : Integrated Amazon SES for email notifications and OAuth for secure Google login, improving user experience and communication.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ },
+ {
+ "id": "rw3x7tapntrt877rbl4pnxz7",
+ "visible": true,
+ "name": "NASA Space Apps Challenge",
+ "description": "A 48-hour, global hackathon powered by NASA open data",
+ "date": "Oct 4–5, 2025",
+ "summary": "Full-Stack Integration: Wired up backend services to a responsive frontend, enabling real-time exploration of Kepler/K2/TESS catalogs and smooth model-scoring UX.
Data Harmonization Pipeline: Cleaned, merged, and standardized multi-mission catalogs into a unified schema, unblocking ML teammates and cutting data-prep time by 60%+ during the hack.
Analytics UI & Upload Flow: Built an upload → validate → score workflow and a clear results dashboard so researchers can triage candidates in minutes, not hours.
Delivery Under Pressure: Coordinated a 5-person multidisciplinary team to ship a working web app in 48 hours , with demo-ready reliability for judging.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://exploranium.vercel.app/dashboard"
+ }
+ },
+ {
+ "id": "i2t6epmx5v7s0d8rqtxsigp3",
+ "visible": true,
+ "name": "Strong Statistics",
+ "description": "Self-hosted strength analytics app using FastAPI and Next.js to visualize Strong app data with full local privacy and active open-source adoption.",
+ "date": "September 2025 - Present",
+ "summary": "Self-Hosted Strength Analytics Platform: Developed strong-statistics , an open-source web app that visualizes detailed workout analytics from the Strong and Hevy fitness app, giving users local control of their training data.
Full-Stack Architecture: Built a modular stack with FastAPI , Next.js , Tailwind CSS , and SQLite , deployed via Docker Compose for seamless self-hosting and persistent local data storage.
Active Open-Source Ecosystem: Published on GitHub with community engagement from global users — external contributors opened feature requests and bug reports, validating real-world adoption and reliability.
Continuous Personal Use & Maintenance: Regularly updated and used in live deployment at lifting.dakheera47.com , tracking hundreds of sets over time with persistent analytics and performance trends.
",
+ "keywords": [],
+ "url": {
+ "label": "",
+ "href": "https://lifting.dakheera47.com/"
+ }
+ }
+ ]
+ },
+ "publications": {
+ "name": "Publications",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": true,
+ "id": "publications",
+ "items": []
+ },
+ "references": {
+ "name": "References",
+ "columns": 1,
+ "separateLinks": true,
+ "visible": false,
+ "id": "references",
+ "items": [
+ {
+ "id": "f2sv5z0cce6ztjl87yuk8fak",
+ "visible": true,
+ "name": "Available upon request",
+ "description": "",
+ "summary": "",
+ "url": {
+ "label": "",
+ "href": ""
+ }
+ }
+ ]
+ },
+ "skills": {
+ "name": "Skills",
+ "columns": 2,
+ "separateLinks": true,
+ "visible": true,
+ "id": "skills",
+ "items": [
+ {
+ "id": "jfgzfcwcg65k9gemuxlfe9m3",
+ "visible": true,
+ "name": "Frontend Development",
+ "description": "",
+ "level": 0,
+ "keywords": [
+ "React",
+ "Next.js",
+ "Tailwind CSS",
+ "Strapi CMS",
+ "Elementor",
+ "GraphQL",
+ "TypeScript",
+ "CI/CD",
+ "PWA Development",
+ "AstroJS",
+ "React Testing Library"
+ ]
+ },
+ {
+ "id": "sk3957foopxir2hw4xzxqahh",
+ "visible": true,
+ "name": "Backend Development",
+ "description": "",
+ "level": 0,
+ "keywords": [
+ "Node.js",
+ "Express.js",
+ "MongoDB",
+ "Supabase",
+ "Firebase",
+ "Docker",
+ "FastAPI",
+ "AWS S3",
+ "AWS SES"
+ ]
+ },
+ {
+ "id": "d9bddwdj6qreknhk644rm0bs",
+ "visible": true,
+ "name": "Leadership and Problem-Solving",
+ "description": "",
+ "level": 0,
+ "keywords": [
+ "Agile Project Management",
+ "Conflict Resolution",
+ "Creative Problem-Solving",
+ "Decision-Making",
+ "Effective Communication",
+ "Adaptability"
+ ]
+ },
+ {
+ "id": "gk4hrky0wnbsbdcmmud48zjh",
+ "visible": true,
+ "name": "Other Programming",
+ "description": "",
+ "level": 0,
+ "keywords": [
+ "Python Scripting",
+ "PyAutoGUI",
+ "Git",
+ "GitHub",
+ "Selenium",
+ "Data Analysis",
+ "Web Scraping",
+ "Data Cleaning"
+ ]
+ }
+ ]
+ },
+ "custom": {}
+ },
+ "metadata": {
+ "template": "onyx",
+ "layout": [
+ [
+ [
+ "summary",
+ "education",
+ "experience",
+ "projects",
+ "references"
+ ],
+ [
+ "profiles",
+ "skills",
+ "certifications",
+ "interests",
+ "languages",
+ "awards",
+ "volunteer",
+ "publications"
+ ]
+ ]
+ ],
+ "css": {
+ "value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
+ "visible": false
+ },
+ "page": {
+ "margin": 34,
+ "format": "a4",
+ "options": {
+ "breakLine": false,
+ "pageNumbers": false
+ }
+ },
+ "theme": {
+ "background": "#ffffff",
+ "text": "#000000",
+ "primary": "#475569"
+ },
+ "typography": {
+ "font": {
+ "family": "IBM Plex Sans",
+ "subset": "latin",
+ "variants": [
+ "regular"
+ ],
+ "size": 13
+ },
+ "lineHeight": 1.75,
+ "hideIcons": false,
+ "underlineLinks": true
+ },
+ "notes": ""
+ }
+}
\ No newline at end of file