diff --git a/CLAUDE.md b/CLAUDE.md index da64dea..6271fa1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -214,6 +214,50 @@ MCP tools return errors as successful responses with error content, not as throw - `https://www.googleapis.com/auth/calendar.events` - `https://www.googleapis.com/auth/calendar` +## Google Tasks API (Optional) + +Google Tasks integration is disabled by default. Enable with `ENABLE_TASKS=true` or `--enable-tasks`. + +### Key Files + +- `src/auth/scopes.ts` - Centralized scope configuration, conditionally adds Tasks scope +- `src/handlers/core/BaseTaskHandler.ts` - Base class for task handlers, extends `BaseToolHandler` +- `src/tools/task-schemas.ts` - Zod schemas for task tools +- `src/types/task-responses.ts` - Response type definitions +- Task handlers: `ListTaskListsHandler`, `ListTasksHandler`, `GetTaskHandler`, `CreateTaskHandler`, `UpdateTaskHandler`, `DeleteTaskHandler` + +### Adding Task Tools + +Task handlers follow the same pattern as calendar handlers but extend `BaseTaskHandler`: + +```typescript +export class MyTaskHandler extends BaseTaskHandler { + async runTool(args: MyTaskInput, accounts: Map) { + const client = this.getClientForAccount(args.account, accounts); + const tasks = this.getTasks(client); // Returns tasks_v1.Tasks client + // ... + } +} +``` + +### Testing Tasks + +Unit tests for task handlers are in `src/tests/unit/handlers/`: +- `ListTaskListsHandler.test.ts` +- `ListTasksHandler.test.ts` +- `GetTaskHandler.test.ts` +- `CreateTaskHandler.test.ts` +- `UpdateTaskHandler.test.ts` +- `DeleteTaskHandler.test.ts` + +Run with: `npm test -- src/tests/unit/handlers/*Task*.test.ts` + +### Tasks API Reference + +- **Scope**: `https://www.googleapis.com/auth/tasks` +- **Resources**: `tasklists` (list, get, insert, update, delete), `tasks` (list, get, insert, update, delete, move, clear) +- **Default Task List**: Use `@default` for the user's default task list + ## Deployment - **npx**: `npx @cocal/google-calendar-mcp` (requires `GOOGLE_OAUTH_CREDENTIALS` env var) diff --git a/README.md b/README.md index 932a061..5a93d46 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ A Model Context Protocol (MCP) server that provides Google Calendar integration - **Free/Busy Queries**: Check availability across calendars - **Smart Scheduling**: Natural language understanding for dates and times - **Intelligent Import**: Add calendar events from images, PDFs, or web links +- **Google Tasks Integration** *(optional)*: Manage tasks alongside calendar events ## Quick Start @@ -188,9 +189,46 @@ Along with the normal capabilities you would expect for a calendar integration y | `list-colors` | List available event colors | | `manage-accounts` | Add, list, or remove connected Google accounts | +### Google Tasks Tools *(Optional)* + +Enable Google Tasks integration with `ENABLE_TASKS=true`. See [Google Tasks Integration](#google-tasks-integration-optional) for setup. + +| Tool | Description | +|------|-------------| +| `list-task-lists` | List all task lists for an account | +| `list-tasks` | List tasks in a task list with filtering | +| `get-task` | Get details of a specific task | +| `create-task` | Create a new task | +| `update-task` | Update task or mark completed (`status: 'completed'`) | +| `delete-task` | Delete a task | + +## Google Tasks Integration *(Optional)* + +Google Tasks integration is disabled by default to keep the OAuth scope minimal. Quick setup: + +1. **Enable the Tasks API** in [Google Cloud Console](https://console.cloud.google.com/apis/library/tasks.googleapis.com) + +2. **Add to your MCP configuration:** + ```json + { + "env": { + "GOOGLE_OAUTH_CREDENTIALS": "/path/to/credentials.json", + "ENABLE_TASKS": "true" + } + } + ``` + +3. **Re-authenticate** with the Tasks scope: + ```bash + ENABLE_TASKS=true npx @cocal/google-calendar-mcp auth + ``` + +See the [Google Tasks Guide](docs/tasks.md) for detailed setup, troubleshooting, and examples. + ## Documentation - [Authentication Setup](docs/authentication.md) - Detailed Google Cloud setup +- [Google Tasks Guide](docs/tasks.md) - Complete setup and troubleshooting for Tasks integration - [Advanced Usage](docs/advanced-usage.md) - Multi-account, batch operations - [Deployment Guide](docs/deployment.md) - HTTP transport, remote access - [Docker Guide](docs/docker.md) - Docker deployment with stdio and HTTP modes @@ -205,6 +243,7 @@ Along with the normal capabilities you would expect for a calendar integration y - `GOOGLE_OAUTH_CREDENTIALS` - Path to OAuth credentials file - `GOOGLE_CALENDAR_MCP_TOKEN_PATH` - Custom token storage location (optional) - `ENABLED_TOOLS` - Comma-separated list of tools to enable (see Tool Filtering below) +- `ENABLE_TASKS` - Set to `true` to enable Google Tasks integration (see [Google Tasks Integration](#google-tasks-integration-optional)) ### Tool Filtering @@ -236,6 +275,8 @@ npx @cocal/google-calendar-mcp start --enable-tools list-events,create-event,get **Available tool names:** `list-calendars`, `list-events`, `search-events`, `get-event`, `list-colors`, `create-event`, `update-event`, `delete-event`, `get-freebusy`, `get-current-time`, `respond-to-event`, `manage-accounts` +**Task tools (when `ENABLE_TASKS=true`):** `list-task-lists`, `list-tasks`, `get-task`, `create-task`, `update-task`, `delete-task` + **Note:** The `manage-accounts` tool is always available regardless of filtering, as it's needed for authentication management. When tool filtering is active, the server provides instructions to the AI assistant listing which tools are disabled. This allows the AI to inform users that additional functionality exists but is currently unavailable, without consuming the full token cost of those tool schemas. diff --git a/docs/tasks.md b/docs/tasks.md new file mode 100644 index 0000000..345b921 --- /dev/null +++ b/docs/tasks.md @@ -0,0 +1,235 @@ +# Google Tasks Integration + +Google Tasks integration allows you to manage tasks alongside your calendar events. This feature is **disabled by default** to keep the OAuth scope minimal. + +## Prerequisites + +Before enabling Tasks integration, you need to complete these steps: + +### 1. Enable the Tasks API in Google Cloud Console + +The Tasks API must be enabled separately from the Calendar API in your Google Cloud project. + +1. Go to the [Google Tasks API page](https://console.cloud.google.com/apis/library/tasks.googleapis.com) +2. Make sure your project is selected in the top bar +3. Click **Enable** +4. Wait a few minutes for the change to propagate + +> **Note**: If you skip this step, you'll see an error like: +> ``` +> Google Tasks API has not been used in project XXXXX before or it is disabled. +> ``` + +### 2. Enable Tasks in the MCP Server + +Tasks must be explicitly enabled when running the server. Choose one of these methods: + +**Option A: Environment Variable (Recommended for MCP clients)** + +Add `ENABLE_TASKS` to your MCP configuration: + +```json +{ + "mcpServers": { + "google-calendar": { + "command": "npx", + "args": ["@cocal/google-calendar-mcp"], + "env": { + "GOOGLE_OAUTH_CREDENTIALS": "/path/to/credentials.json", + "ENABLE_TASKS": "true" + } + } + } +} +``` + +**Option B: CLI Flag** + +```bash +npx @cocal/google-calendar-mcp start --enable-tasks +``` + +### 3. Re-authenticate with Tasks Scope + +After enabling Tasks, you must re-authenticate to grant the additional OAuth scope. The server stores which scopes were granted, and will prompt for re-authentication if Tasks is enabled but the scope is missing. + +**For npx users:** +```bash +ENABLE_TASKS=true npx @cocal/google-calendar-mcp auth +``` + +**For local installation:** +```bash +ENABLE_TASKS=true npm run auth +``` + +During the OAuth flow, Google will show that the app is requesting additional access for Tasks: +- "Create, edit, organize, and delete all your tasks" + +Click **Continue** to grant this permission. + +> **Important**: You must include `ENABLE_TASKS=true` when running the auth command, not just when running the server. Otherwise, the auth flow won't request the Tasks scope. + +### 4. Restart Your MCP Client + +After re-authenticating, restart Claude Desktop (or your MCP client) to pick up the new configuration. + +## Available Tools + +Once enabled, these task management tools become available: + +| Tool | Description | +|------|-------------| +| `list-task-lists` | List all task lists for an account | +| `create-task-list` | Create a new task list | +| `list-tasks` | List tasks in a task list with filtering by status and due date | +| `get-task` | Get details of a specific task | +| `create-task` | Create a new task with title, notes, and due date. Supports recurring tasks. | +| `update-task` | Update task properties or mark as completed (`status: 'completed'`) | +| `delete-task` | Delete a task | + +## Example Usage + +**Create a task:** +``` +Create a task to "Review quarterly report" due next Friday +``` + +**List tasks:** +``` +Show me all my incomplete tasks +``` + +**Complete a task (via update-task):** +``` +Mark the "Send invoices" task as complete +``` +Note: Use `update-task` with `status: 'completed'` to mark tasks done. + +**Organize tasks:** +``` +What tasks do I have due this week? +``` + +**Create a task list:** +``` +Create a new task list called "Project Alpha Tasks" +``` + +**Create recurring tasks:** +``` +Create a daily task "Check emails" for the next 5 days +Create a weekly task "Team meeting" every Monday for the next 4 weeks +Create a monthly task "Submit report" on the 1st of each month for 6 months +``` + +## Important Limitations + +### Time-Specific Tasks Not Supported + +**Google Tasks API only supports date-level due dates, not specific times.** This is a fundamental limitation of the Google Tasks API itself, not this MCP server. + +- When you create a task with a specific time (e.g., `2024-01-15T14:00:00Z`), the time component is stored but **not used for scheduling or display** +- Tasks always appear as all-day items in Google Calendar and Google Tasks +- This limitation has been requested by users for years but remains unaddressed by Google + +**Workaround:** If you need tasks at specific times, consider: +1. Adding the time to the task notes/description (e.g., "Due at 2:00 PM") +2. Using calendar events instead of tasks for time-specific items + +### Recurring Tasks + +The Google Tasks API does not natively support recurring tasks. This MCP server implements recurring tasks by **creating multiple individual tasks** with sequential due dates. + +**Behavior:** +- Each task in the series is independent (completing one doesn't affect others) +- Tasks are numbered (e.g., "Task Title (#1/5)", "Task Title (#2/5)") +- Maximum 365 occurrences per recurrence pattern + +**Supported Frequencies:** +- Daily (e.g., every 1-999 days) +- Weekly (e.g., every 1-999 weeks) +- Monthly (e.g., every 1-999 months) +- Yearly (e.g., every 1-999 years) + +**Recurrence Options:** +- `count`: Number of occurrences to create (max 365) +- `until`: End date in YYYY-MM-DD format +- `interval`: Repeat every N days/weeks/months/years (default: 1) + +## Troubleshooting + +### "Access denied: Request had insufficient authentication scopes" + +This error means your OAuth token doesn't include the Tasks scope. Re-authenticate with Tasks enabled: + +```bash +ENABLE_TASKS=true npm run auth +# or for npx: +ENABLE_TASKS=true npx @cocal/google-calendar-mcp auth +``` + +### "Google Tasks API has not been used in project XXXXX" + +The Tasks API isn't enabled in your Google Cloud project. Visit: +``` +https://console.cloud.google.com/apis/library/tasks.googleapis.com +``` + +Enable the API and wait a few minutes before retrying. + +### "Tasks feature enabled but no scope information saved" + +Your existing token was created before scope tracking was implemented. Re-authenticate to update: + +```bash +ENABLE_TASKS=true npm run auth +``` + +### Tasks tools not appearing + +Make sure: +1. `ENABLE_TASKS=true` is set in your MCP client configuration +2. You've restarted your MCP client after changing the configuration +3. If using `ENABLED_TOOLS` filtering, include the task tools you want + +## Multi-Account Support + +Tasks integration works with multiple accounts. Each account needs to be authenticated with the Tasks scope separately. + +To authenticate additional accounts with Tasks: + +```bash +ENABLE_TASKS=true GOOGLE_ACCOUNT_MODE=work npm run auth +``` + +Or use the `manage-accounts` tool in chat after enabling Tasks in your MCP configuration. + +## Technical Details + +### OAuth Scopes + +When Tasks is enabled, the server requests these scopes: +- `https://www.googleapis.com/auth/calendar` (Calendar) +- `https://www.googleapis.com/auth/tasks` (Tasks) + +Scope information is stored in the token file (`~/.config/google-calendar-mcp/tokens.json`) under `granted_scopes` for each account. + +### Default Task List + +Use `@default` as the task list ID to access the user's default task list: + +``` +List tasks from my default task list +``` + +### Task Properties + +Tasks support these properties: +- **title**: Task title (required, max 1024 characters) +- **notes**: Description/notes (max 8192 characters) +- **due**: Due date in RFC 3339 format (e.g., `2024-01-15T00:00:00Z` or date-only `2024-01-15`) + - ⚠️ **Note:** Google Tasks only uses the date portion; time component is ignored +- **status**: `needsAction` (incomplete) or `completed` +- **parent**: Parent task ID for subtasks +- **recurrence**: Optional recurrence pattern for creating multiple tasks (daily, weekly, monthly, yearly) diff --git a/package-lock.json b/package-lock.json index ded7aec..fbf4c27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,8 @@ }, "node_modules/@ampproject/remapping": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -45,6 +47,8 @@ }, "node_modules/@anthropic-ai/sdk": { "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.52.0.tgz", + "integrity": "sha512-d4c+fg+xy9e46c8+YnrrgIQR45CZlAi7PwdzIfDXDM6ACxEZli1/fxhURsq30ZpMZy6LvSkr41jGq5aF5TD7rQ==", "dev": true, "license": "MIT", "bin": { @@ -53,6 +57,8 @@ }, "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": { @@ -60,7 +66,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", + "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": { @@ -68,11 +76,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.5", + "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.27.3" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -82,12 +92,14 @@ } }, "node_modules/@babel/types": { - "version": "7.27.6", + "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.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -95,138 +107,171 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", + "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": [ - "arm64" + "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "aix" ], "engines": { "node": ">=18" } }, - "node_modules/@google-cloud/local-auth": { - "version": "3.0.1", - "license": "Apache-2.0", - "dependencies": { - "arrify": "^2.0.1", - "google-auth-library": "^9.0.0", - "open": "^7.0.3", - "server-destroy": "^1.0.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", + "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": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", + "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": ">=8" + "node": ">=18" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", + "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", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", + "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": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", + "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": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", + "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", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.12.3", + "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", - "dependencies": { - "ajv": "^6.12.6", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { "node": ">=18" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", + "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": ">=14" + "node": ">=18" } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.43.0", + "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" ], @@ -234,1472 +279,3074 @@ "license": "MIT", "optional": true, "os": [ - "darwin" - ] + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@types/chai": { - "version": "5.2.2", + "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", - "dependencies": { - "@types/deep-eql": "*" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.1", + "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", - "dependencies": { - "undici-types": "~6.21.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", + "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", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@vitest/coverage-v8": { - "version": "3.2.3", + "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", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "3.2.3", - "vitest": "3.2.3" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@vitest/expect": { - "version": "3.2.3", + "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", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.3", - "@vitest/utils": "3.2.3", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@vitest/mocker": { - "version": "3.2.3", + "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", - "dependencies": { - "@vitest/spy": "3.2.3", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.3", + "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", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@vitest/runner": { - "version": "3.2.3", + "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", - "dependencies": { - "@vitest/utils": "3.2.3", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@vitest/snapshot": { - "version": "3.2.3", + "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", - "dependencies": { - "@vitest/pretty-format": "3.2.3", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@vitest/spy": { - "version": "3.2.3", + "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", - "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@vitest/utils": { - "version": "3.2.3", + "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", - "dependencies": { - "@vitest/pretty-format": "3.2.3", - "loupe": "^3.1.3", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/abort-controller": { - "version": "3.0.0", + "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", - "dependencies": { - "event-target-shim": "^5.0.0" - }, + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=6.5" + "node": ">=18" } }, - "node_modules/accepts": { - "version": "2.0.0", + "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", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/agent-base": { - "version": "7.1.3", + "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": ">= 14" + "node": ">=18" } }, - "node_modules/agentkeepalive": { - "version": "4.6.0", + "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", - "dependencies": { - "humanize-ms": "^1.2.1" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 8.0.0" + "node": ">=18" } }, - "node_modules/ajv": { - "version": "6.12.6", + "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/@google-cloud/local-auth": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/local-auth/-/local-auth-3.0.1.tgz", + "integrity": "sha512-YJ3GFbksfHyEarbVHPSCzhKpjbnlAhdzg2SEf79l6ODukrSM1qUOqfopY232Xkw26huKSndyzmJz+A6b2WYn7Q==", + "license": "Apache-2.0", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "arrify": "^2.0.1", + "google-auth-library": "^9.0.0", + "open": "^7.0.3", + "server-destroy": "^1.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "dev": true, + "node_modules/@hono/node-server": { + "version": "1.19.8", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.8.tgz", + "integrity": "sha512-0/g2lIOPzX8f3vzW1ggQgvG5mjtFBDBHFAzI5SFAi2DzSqS9luJwqg9T6O/gKYLi+inS7eNxBeIFkkghIPvrMA==", "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18.14.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "peerDependencies": { + "hono": "^4" } }, - "node_modules/ansi-styles": { - "version": "6.2.1", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/arrify": { - "version": "2.0.1", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/assertion-error": { - "version": "2.0.1", + "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", - "engines": { - "node": ">=12" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", + "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", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "estree-walker": "^3.0.3", - "js-tokens": "^9.0.1" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/asynckit": { - "version": "0.4.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/balanced-match": { - "version": "1.0.2", + "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" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "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/bignumber.js": { - "version": "9.3.0", "license": "MIT", - "engines": { - "node": "*" + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/body-parser": { - "version": "2.2.0", + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.25.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.2.tgz", + "integrity": "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==", "license": "MIT", "dependencies": { - "bytes": "^3.1.2", + "@hono/node-server": "^1.19.7", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } } }, - "node_modules/brace-expansion": { - "version": "2.0.2", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "license": "BSD-3-Clause" - }, - "node_modules/bytes": { - "version": "3.1.2", - "license": "MIT", + "optional": true, "engines": { - "node": ">= 0.8" + "node": ">=14" } }, - "node_modules/cac": { - "version": "6.7.14", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", + "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", + "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", + "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", + "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", + "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", + "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", + "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", + "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", + "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", + "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", + "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", + "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", + "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", + "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", + "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", + "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", + "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", + "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", + "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", + "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", + "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", + "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", + "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", + "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", + "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "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/node": { + "version": "20.19.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.27.tgz", + "integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", + "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^1.0.2", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.17", + "magicast": "^0.3.5", + "std-env": "^3.9.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "3.2.4", + "vitest": "3.2.4" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.10.tgz", + "integrity": "sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "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/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "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/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=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/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "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/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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/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.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "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/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.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/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/call-bound": { - "version": "1.0.4", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "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/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "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/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-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "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": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/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/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "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/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/chai": { - "version": "5.2.0", - "dev": true, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, "engines": { - "node": ">=12" + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" } }, - "node_modules/check-error": { - "version": "2.1.1", + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "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": ">= 16" + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "delayed-stream": "~1.0.0" + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/content-disposition": { - "version": "1.0.0", + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "5.2.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.6" + "node": ">= 6" } }, - "node_modules/content-type": { - "version": "1.0.5", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "dev": true, + "license": "MIT" }, - "node_modules/cookie": { - "version": "0.7.2", + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.5", + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "license": "MIT", "dependencies": { - "object-assign": "^4", - "vary": "^1" + "mime-db": "1.52.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 0.6" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" }, "engines": { - "node": ">= 8" + "node": ">= 12.20" } }, - "node_modules/debug": { - "version": "4.4.1", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.6" } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "dev": true, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.8" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", + "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": ">=0.4.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/depd": { - "version": "2.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", - "engines": { - "node": ">= 0.8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "license": "MIT", + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=14" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", "license": "Apache-2.0", "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "license": "MIT", + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=14" } }, - "node_modules/es-errors": { + "node_modules/get-intrinsic": { "version": "1.3.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "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/es-set-tostringtag": { - "version": "2.1.0", - "dev": true, + "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": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, - "node_modules/esbuild": { - "version": "0.25.5", + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=18" + "bin": { + "glob": "dist/esm/bin.mjs" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "dev": true, - "license": "MIT", + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", "dependencies": { - "@types/estree": "^1.0.0" + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" } }, - "node_modules/etag": { - "version": "1.8.1", - "license": "MIT", + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", "engines": { - "node": ">= 0.6" + "node": ">=14" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "dev": true, - "license": "MIT", + "node_modules/googleapis": { + "version": "144.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-144.0.0.tgz", + "integrity": "sha512-ELcWOXtJxjPX4vsKMh+7V+jZvgPwYMlEhQFiu2sa9Qmt5veX8nwXPksOWGGN6Zk4xCiLygUyaz7xGtcMO+Onxw==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, "engines": { - "node": ">=6" + "node": ">=14.0.0" } }, - "node_modules/eventsource": { - "version": "3.0.7", - "license": "MIT", + "node_modules/googleapis-common": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", + "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", + "license": "Apache-2.0", "dependencies": { - "eventsource-parser": "^3.0.1" + "extend": "^3.0.2", + "gaxios": "^6.0.3", + "google-auth-library": "^9.7.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^9.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/eventsource-parser": { - "version": "3.0.2", + "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": ">=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/expect-type": { - "version": "1.2.1", - "dev": true, - "license": "Apache-2.0", + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, - "node_modules/express": { - "version": "5.1.0", + "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", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=8" } }, - "node_modules/express-rate-limit": { - "version": "7.5.0", + "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": ">= 16" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": "^4.11 || 5 || ^5.0.0-beta.1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/extend": { - "version": "3.0.2", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.4.6", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" + "dependencies": { + "has-symbols": "^1.0.3" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/finalhandler": { - "version": "2.1.0", + "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": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" + "function-bind": "^1.1.2" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, + "node_modules/hono": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.3.tgz", + "integrity": "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==", + "license": "MIT", + "peer": true, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16.9.0" } }, - "node_modules/form-data": { - "version": "4.0.4", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, + "license": "MIT" + }, + "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": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { - "node": ">= 6" + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "dev": true, - "license": "MIT" - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "dev": true, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, "engines": { - "node": ">= 0.6" + "node": ">= 14" } }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dev": true, "license": "MIT", "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" + "ms": "^2.0.0" } }, - "node_modules/formdata-node": { - "version": "4.4.1", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "license": "MIT", "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">= 12.20" + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/forwarded": { - "version": "0.2.0", + "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/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.6" + "node": ">= 0.10" } }, - "node_modules/fresh": { - "version": "2.0.0", + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, "engines": { - "node": ">= 0.8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fsevents": { - "version": "2.3.3", + "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", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=8" } }, - "node_modules/function-bind": { - "version": "1.1.2", + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "license": "MIT", + "engines": { + "node": ">=8" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gaxios": { - "version": "6.7.1", - "license": "Apache-2.0", + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9", - "uuid": "^9.0.1" + "is-docker": "^2.0.0" }, "engines": { - "node": ">=14" + "node": ">=8" } }, - "node_modules/gcp-metadata": { - "version": "6.1.1", - "license": "Apache-2.0", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "gaxios": "^6.1.1", - "google-logging-utils": "^0.0.2", - "json-bigint": "^1.0.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=14" + "node": ">=10" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "license": "MIT", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", "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" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/get-proto": { - "version": "1.0.1", - "license": "MIT", + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/glob": { - "version": "10.4.5", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "@isaacs/cliui": "^8.0.2" }, "funding": { "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/google-auth-library": { - "version": "9.15.1", - "license": "Apache-2.0", - "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^6.1.1", - "gcp-metadata": "^6.1.0", - "gtoken": "^7.0.0", - "jws": "^4.0.0" }, - "engines": { - "node": ">=14" + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/google-logging-utils": { - "version": "0.0.2", - "license": "Apache-2.0", - "engines": { - "node": ">=14" + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" } }, - "node_modules/googleapis": { - "version": "144.0.0", - "license": "Apache-2.0", + "node_modules/js-tiktoken": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", + "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==", + "dev": true, + "license": "MIT", "dependencies": { - "google-auth-library": "^9.0.0", - "googleapis-common": "^7.0.0" - }, - "engines": { - "node": ">=14.0.0" + "base64-js": "^1.5.1" } }, - "node_modules/googleapis-common": { - "version": "7.2.0", - "license": "Apache-2.0", + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", "dependencies": { - "extend": "^3.0.2", - "gaxios": "^6.0.3", - "google-auth-library": "^9.7.0", - "qs": "^6.7.0", - "url-template": "^2.0.8", - "uuid": "^9.0.0" - }, - "engines": { - "node": ">=14.0.0" + "bignumber.js": "^9.0.0" } }, - "node_modules/gopd": { - "version": "1.2.0", + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" } }, - "node_modules/gtoken": { - "version": "7.1.0", + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "dependencies": { - "gaxios": "^6.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14.0.0" + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" } }, - "node_modules/has-flag": { - "version": "4.0.0", + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/has-symbols": { - "version": "1.1.0", + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", "dependencies": { - "has-symbols": "^1.0.3" + "semver": "^7.5.3" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hasown": { - "version": "2.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", - "dependencies": { - "function-bind": "^1.1.2" - }, "engines": { "node": ">= 0.4" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.0", + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", - "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/http-errors/node_modules/statuses": { - "version": "2.0.1", + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, "engines": { - "node": ">= 14" + "node": ">= 0.6" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "dev": true, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { - "ms": "^2.0.0" + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "license": "MIT", + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "license": "MIT", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", "engines": { - "node": ">= 0.10" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/is-docker": { - "version": "2.2.1", + "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": { - "is-docker": "cli.js" + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/is-promise": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/is-stream": { - "version": "2.0.1", + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10.5.0" } }, - "node_modules/is-wsl": { - "version": "2.2.0", + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=8" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/isexe": { - "version": "2.0.0", - "license": "ISC" + "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/istanbul-lib-coverage": { - "version": "3.2.2", - "dev": true, - "license": "BSD-3-Clause", + "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": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "dev": true, - "license": "BSD-3-Clause", + "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": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "ee-first": "1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "dev": true, - "license": "BSD-3-Clause", + "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": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" + "wrappy": "1" } }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jackspeak": { - "version": "3.4.3", + "node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "Apache-2.0", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } } }, - "node_modules/js-tiktoken": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", - "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==", + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", "dev": true, "license": "MIT", "dependencies": { - "base64-js": "^1.5.1" + "undici-types": "~5.26.4" } }, - "node_modules/js-tokens": { - "version": "9.0.1", + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true, "license": "MIT" }, - "node_modules/json-bigint": { - "version": "1.0.0", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "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", - "dependencies": { - "bignumber.js": "^9.0.0" + "engines": { + "node": ">= 0.8" } }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "license": "MIT" - }, - "node_modules/jwa": { - "version": "2.0.1", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jws": { - "version": "4.0.0", + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/loupe": { - "version": "3.1.4", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, - "node_modules/magic-string": { - "version": "0.30.17", + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "engines": { + "node": ">= 14.16" } }, - "node_modules/magicast": { - "version": "0.3.5", + "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": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } + "license": "ISC" }, - "node_modules/make-dir": { - "version": "4.0.0", + "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", - "dependencies": { - "semver": "^7.5.3" - }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=16.20.0" } }, - "node_modules/media-typer": { - "version": "1.1.0", + "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": ">= 0.8" + "node": "^10 || ^12 || >=14" } }, - "node_modules/merge-descriptors": { - "version": "2.0.0", + "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": ">=18" + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mime-db": { - "version": "1.54.0", + "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/mime-types": { - "version": "3.0.1", + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.10" } }, - "node_modules/minimatch": { - "version": "9.0.5", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", + "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.55.1", + "@rollup/rollup-android-arm64": "4.55.1", + "@rollup/rollup-darwin-arm64": "4.55.1", + "@rollup/rollup-darwin-x64": "4.55.1", + "@rollup/rollup-freebsd-arm64": "4.55.1", + "@rollup/rollup-freebsd-x64": "4.55.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", + "@rollup/rollup-linux-arm-musleabihf": "4.55.1", + "@rollup/rollup-linux-arm64-gnu": "4.55.1", + "@rollup/rollup-linux-arm64-musl": "4.55.1", + "@rollup/rollup-linux-loong64-gnu": "4.55.1", + "@rollup/rollup-linux-loong64-musl": "4.55.1", + "@rollup/rollup-linux-ppc64-gnu": "4.55.1", + "@rollup/rollup-linux-ppc64-musl": "4.55.1", + "@rollup/rollup-linux-riscv64-gnu": "4.55.1", + "@rollup/rollup-linux-riscv64-musl": "4.55.1", + "@rollup/rollup-linux-s390x-gnu": "4.55.1", + "@rollup/rollup-linux-x64-gnu": "4.55.1", + "@rollup/rollup-linux-x64-musl": "4.55.1", + "@rollup/rollup-openbsd-x64": "4.55.1", + "@rollup/rollup-openharmony-arm64": "4.55.1", + "@rollup/rollup-win32-arm64-msvc": "4.55.1", + "@rollup/rollup-win32-ia32-msvc": "4.55.1", + "@rollup/rollup-win32-x64-gnu": "4.55.1", + "@rollup/rollup-win32-x64-msvc": "4.55.1", + "fsevents": "~2.3.2" } }, - "node_modules/minipass": { - "version": "7.1.2", - "dev": true, - "license": "ISC", + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 18" } }, - "node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "dev": true, + "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/ai" + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } ], - "license": "MIT", + "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/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": { - "nanoid": "bin/nanoid.cjs" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/negotiator": { - "version": "1.0.0", + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/node-domexception": { - "version": "1.0.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } + "node_modules/server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "license": "ISC" }, - "node_modules/node-fetch": { - "version": "2.7.0", + "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/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", "dependencies": { - "whatwg-url": "^5.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "node": ">=8" } }, - "node_modules/object-assign": { - "version": "4.1.1", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/object-inspect": { - "version": "1.13.4", + "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" }, @@ -1707,849 +3354,1002 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/on-finished": { - "version": "2.4.1", + "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": { - "ee-first": "1.1.1" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "license": "ISC", - "dependencies": { - "wrappy": "1" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/open": { - "version": "7.4.2", + "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": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/openai": { - "version": "4.104.0", - "dev": true, - "license": "Apache-2.0", + "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": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, - "bin": { - "openai": "bin/cli" + "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" }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.23.8" + "engines": { + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.112", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "dev": true, - "license": "MIT" - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/path-scurry": { - "version": "1.11.1", + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, + "license": "ISC", "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "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": "ISC" - }, - "node_modules/path-to-regexp": { - "version": "8.2.0", - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">=16" + "node": ">=0.10.0" } }, - "node_modules/pathe": { - "version": "2.0.3", + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, - "node_modules/pathval": { - "version": "2.0.0", - "dev": true, + "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": ">= 14.16" + "node": ">= 0.8" } }, - "node_modules/picocolors": { - "version": "1.1.1", + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/picomatch": { - "version": "4.0.2", + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkce-challenge": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=16.20.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss": { - "version": "8.5.6", - "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" - } - ], + "node_modules/string-width-cjs": { + "name": "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": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=8" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", + "node_modules/string-width-cjs/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", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/punycode": { - "version": "2.3.1", + "node_modules/string-width-cjs/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/string-width-cjs/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": ">=6" + "node": ">=8" } }, - "node_modules/qs": { - "version": "6.14.0", - "license": "BSD-3-Clause", + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", "dependencies": { - "side-channel": "^1.1.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=0.6" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/range-parser": { - "version": "1.2.1", + "node_modules/strip-ansi-cjs": { + "name": "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": ">= 0.6" + "node": ">=8" } }, - "node_modules/raw-body": { - "version": "3.0.0", + "node_modules/strip-ansi-cjs/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", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" - }, "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/rollup": { - "version": "4.43.0", + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "js-tokens": "^9.0.1" }, - "bin": { - "rollup": "dist/bin/rollup" + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "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": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.43.0", - "@rollup/rollup-android-arm64": "4.43.0", - "@rollup/rollup-darwin-arm64": "4.43.0", - "@rollup/rollup-darwin-x64": "4.43.0", - "@rollup/rollup-freebsd-arm64": "4.43.0", - "@rollup/rollup-freebsd-x64": "4.43.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.43.0", - "@rollup/rollup-linux-arm-musleabihf": "4.43.0", - "@rollup/rollup-linux-arm64-gnu": "4.43.0", - "@rollup/rollup-linux-arm64-musl": "4.43.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.43.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-musl": "4.43.0", - "@rollup/rollup-linux-s390x-gnu": "4.43.0", - "@rollup/rollup-linux-x64-gnu": "4.43.0", - "@rollup/rollup-linux-x64-musl": "4.43.0", - "@rollup/rollup-win32-arm64-msvc": "4.43.0", - "@rollup/rollup-win32-ia32-msvc": "4.43.0", - "@rollup/rollup-win32-x64-msvc": "4.43.0", - "fsevents": "~2.3.2" + "node": ">=8" } }, - "node_modules/rollup/node_modules/@types/estree": { - "version": "1.0.7", + "node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", "dev": true, - "license": "MIT" - }, - "node_modules/router": { - "version": "2.2.0", - "license": "MIT", + "license": "ISC", "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" }, "engines": { - "node": ">= 18" + "node": ">=18" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, "license": "MIT" }, - "node_modules/safer-buffer": { - "version": "2.1.2", + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, "license": "MIT" }, - "node_modules/semver": { - "version": "7.7.2", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=10" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/send": { - "version": "1.2.0", + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, "engines": { - "node": ">= 18" + "node": "^18.0.0 || >=20.0.0" } }, - "node_modules/serve-static": { - "version": "2.2.0", + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, "engines": { - "node": ">= 18" + "node": ">=14.0.0" } }, - "node_modules/server-destroy": { - "version": "1.0.1", - "license": "ISC" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, "engines": { - "node": ">=8" + "node": ">=14.0.0" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", + "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": ">=8" + "node": ">=0.6" } }, - "node_modules/side-channel": { - "version": "1.1.0", + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { - "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" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "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" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=14.17" } }, - "node_modules/side-channel-list": { + "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==", + "dev": 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/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", + "license": "BSD" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/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", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": ">= 0.4" + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "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/side-channel-weakmap": { - "version": "1.0.2", + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, "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" + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" }, "engines": { - "node": ">= 0.4" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/vitest" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/source-map-js": { - "version": "1.2.1", + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "node_modules/stackback": { - "version": "0.0.2", + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.2", "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/std-env": { - "version": "3.9.0", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width": { - "version": "5.1.2", + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=18" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/strip-literal": { - "version": "3.0.0", + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/supports-color": { - "version": "7.2.0", + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/test-exclude": { - "version": "7.0.1", + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" } }, - "node_modules/tinybench": { - "version": "2.9.0", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=18" } }, - "node_modules/tinypool": { - "version": "1.1.1", + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": ">=18" } }, - "node_modules/tinyrainbow": { - "version": "2.0.0", + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14.0.0" + "node": ">=18" } }, - "node_modules/tinyspy": { - "version": "4.0.3", + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=14.0.0" + "node": ">=18" } }, - "node_modules/toidentifier": { - "version": "1.0.1", + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=0.6" + "node": ">=18" } }, - "node_modules/tr46": { - "version": "0.0.3", - "license": "MIT" - }, - "node_modules/type-is": { - "version": "2.0.1", + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/typescript": { - "version": "5.8.3", + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=14.17" + "node": ">=18" } }, - "node_modules/undici-types": { - "version": "6.21.0", + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" } }, - "node_modules/url-template": { - "version": "2.0.8", - "license": "BSD" - }, - "node_modules/uuid": { - "version": "9.0.1", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" ], + "dev": true, "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/vary": { - "version": "1.1.2", + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/vite": { - "version": "6.3.5", + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], "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" - }, + "optional": true, + "os": [ + "win32" + ], "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": ">=18" } }, - "node_modules/vite-node": { - "version": "3.2.3", + "node_modules/vite/node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, "bin": { - "vite-node": "vite-node.mjs" + "esbuild": "bin/esbuild" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": ">=18" }, - "funding": { - "url": "https://opencollective.com/vitest" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/vitest": { - "version": "3.2.3", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.3", - "@vitest/mocker": "3.2.3", - "@vitest/pretty-format": "^3.2.3", - "@vitest/runner": "3.2.3", - "@vitest/snapshot": "3.2.3", - "@vitest/spy": "3.2.3", - "@vitest/utils": "3.2.3", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", @@ -2560,10 +4360,10 @@ "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", - "tinypool": "^1.1.0", + "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.3", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -2579,8 +4379,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.3", - "@vitest/ui": "3.2.3", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, @@ -2610,6 +4410,8 @@ }, "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", "dev": true, "license": "MIT", "engines": { @@ -2618,10 +4420,14 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, "node_modules/whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -2630,6 +4436,8 @@ }, "node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -2643,6 +4451,8 @@ }, "node_modules/why-is-node-running": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { @@ -2658,6 +4468,8 @@ }, "node_modules/wrap-ansi": { "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2675,6 +4487,8 @@ "node_modules/wrap-ansi-cjs": { "name": "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": { @@ -2691,6 +4505,8 @@ }, "node_modules/wrap-ansi-cjs/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": { @@ -2699,6 +4515,8 @@ }, "node_modules/wrap-ansi-cjs/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": { @@ -2713,11 +4531,15 @@ }, "node_modules/wrap-ansi-cjs/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/wrap-ansi-cjs/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": { @@ -2731,6 +4553,8 @@ }, "node_modules/wrap-ansi-cjs/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": { @@ -2742,20 +4566,26 @@ }, "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/zod": { - "version": "3.25.67", + "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" } }, "node_modules/zod-to-json-schema": { - "version": "3.24.5", + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", "license": "ISC", "peerDependencies": { - "zod": "^3.24.1" + "zod": "^3.25 || ^4" } } } diff --git a/scripts/dev.js b/scripts/dev.js index 69c4fb5..85f5d9c 100755 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -33,12 +33,6 @@ const commands = { args: ['build/auth-server.js'], env: { GOOGLE_ACCOUNT_MODE: 'normal' } }, - 'auth:test': { - description: 'Authenticate test account', - cmd: 'node', - args: ['build/auth-server.js'], - env: { GOOGLE_ACCOUNT_MODE: 'test' } - }, 'auth:test-primary': { description: 'Authenticate test-primary account (for multi-account testing)', cmd: 'node', @@ -61,11 +55,6 @@ const commands = { cmd: 'node', args: ['scripts/account-manager.js', 'clear', 'normal'] }, - 'account:clear:test': { - description: 'Clear test account tokens', - cmd: 'node', - args: ['scripts/account-manager.js', 'clear', 'test'] - }, // Unit Testing 'test': { diff --git a/src/auth-server.ts b/src/auth-server.ts index af7f9fe..46ff00b 100644 --- a/src/auth-server.ts +++ b/src/auth-server.ts @@ -12,8 +12,9 @@ async function runAuthServer() { let authServer: AuthServer | null = null; // Keep reference for cleanup try { const oauth2Client = await initializeOAuth2Client(); - - authServer = new AuthServer(oauth2Client); + + const enableTasks = process.env.ENABLE_TASKS === 'true'; + authServer = new AuthServer(oauth2Client, enableTasks); const success = await authServer.start(true); diff --git a/src/auth/scopes.ts b/src/auth/scopes.ts new file mode 100644 index 0000000..d3d866f --- /dev/null +++ b/src/auth/scopes.ts @@ -0,0 +1,73 @@ +/** + * OAuth 2.0 scope configuration for Google APIs + * + * This module manages the OAuth scopes required by the MCP server. + * Scopes are requested conditionally based on enabled features. + */ + +// Calendar API scope - always required +export const CALENDAR_SCOPE = 'https://www.googleapis.com/auth/calendar'; + +// Tasks API scope - only required when Tasks feature is enabled +export const TASKS_SCOPE = 'https://www.googleapis.com/auth/tasks'; + +/** + * Get the OAuth scopes required for the current configuration. + * + * @param enableTasks - Whether the Tasks feature is enabled + * @returns Array of OAuth scope URLs + */ +export function getRequiredScopes(enableTasks = false): string[] { + const scopes = [CALENDAR_SCOPE]; + + if (enableTasks) { + scopes.push(TASKS_SCOPE); + } + + return scopes; +} + +/** + * Check if the granted scopes satisfy the required scopes. + * + * @param grantedScopes - Array of scopes that were granted during auth + * @param requiredScopes - Array of scopes required for current config + * @returns Object with validation result and missing scopes + */ +export function validateScopes( + grantedScopes: string[] | undefined, + requiredScopes: string[] +): { valid: boolean; missingScopes: string[] } { + if (!grantedScopes || grantedScopes.length === 0) { + // No scope information available - assume valid for backward compatibility + // Tokens created before scope tracking won't have this field + return { valid: true, missingScopes: [] }; + } + + const grantedSet = new Set(grantedScopes); + const missingScopes = requiredScopes.filter(scope => !grantedSet.has(scope)); + + return { + valid: missingScopes.length === 0, + missingScopes + }; +} + +/** + * Get a human-readable description of what permissions are missing. + * + * @param missingScopes - Array of missing OAuth scope URLs + * @returns Human-readable message + */ +export function getMissingScopesMessage(missingScopes: string[]): string { + const scopeDescriptions: Record = { + [CALENDAR_SCOPE]: 'Google Calendar', + [TASKS_SCOPE]: 'Google Tasks' + }; + + const missingFeatures = missingScopes + .map(scope => scopeDescriptions[scope] || scope) + .join(', '); + + return `Your current authentication is missing permissions for: ${missingFeatures}. Please re-authenticate to grant access.`; +} diff --git a/src/auth/server.ts b/src/auth/server.ts index 5892cd9..56e3c39 100644 --- a/src/auth/server.ts +++ b/src/auth/server.ts @@ -6,6 +6,7 @@ import open from 'open'; import { loadCredentials } from './client.js'; import { getAccountMode } from './utils.js'; import { renderAuthSuccess, renderAuthError, renderAuthLanding, loadWebFile } from '../web/templates.js'; +import { getRequiredScopes, validateScopes, getMissingScopesMessage } from './scopes.js'; export interface StartForMcpToolResult { success: boolean; @@ -24,11 +25,13 @@ export class AuthServer { public authCompletedSuccessfully = false; // Flag for standalone script private mcpToolTimeout: ReturnType | null = null; // Timeout for MCP tool auth flow private autoShutdownOnSuccess = false; // Whether to auto-shutdown after successful auth + private enableTasks = false; // Whether Google Tasks integration is enabled - constructor(oauth2Client: OAuth2Client) { + constructor(oauth2Client: OAuth2Client, enableTasks = false) { this.baseOAuth2Client = oauth2Client; this.tokenManager = new TokenManager(oauth2Client); this.portRange = { start: 3500, end: 3505 }; + this.enableTasks = enableTasks; } private createServer(): http.Server { @@ -44,7 +47,7 @@ export class AuthServer { } else if (url.pathname === '/') { // Root route - show auth link const clientForUrl = this.flowOAuth2Client || this.baseOAuth2Client; - const scopes = ['https://www.googleapis.com/auth/calendar']; + const scopes = getRequiredScopes(this.enableTasks); const authUrl = clientForUrl.generateAuthUrl({ access_type: 'offline', scope: scopes, @@ -83,7 +86,9 @@ export class AuthServer { try { const { tokens } = await this.flowOAuth2Client.getToken(code); - await this.tokenManager.saveTokens(tokens); + // Save tokens with the scopes that were requested during this auth flow + const requestedScopes = getRequiredScopes(this.enableTasks); + await this.tokenManager.saveTokens(tokens, undefined, requestedScopes); this.authCompletedSuccessfully = true; const tokenPath = this.tokenManager.getTokenPath(); @@ -149,8 +154,24 @@ export class AuthServer { private async startWithTimeout(openBrowser = true): Promise { if (await this.tokenManager.validateTokens()) { - this.authCompletedSuccessfully = true; - return true; + // Token is valid, but check if it has the required scopes + const grantedScopes = await this.tokenManager.getGrantedScopes(); + const requiredScopes = getRequiredScopes(this.enableTasks); + + // If Tasks is enabled but we have no scope info, force re-auth to get Tasks scope + if (this.enableTasks && (!grantedScopes || grantedScopes.length === 0)) { + process.stderr.write(`\nTasks feature enabled but no scope information saved. Re-authentication required.\n\n`); + } else { + const { valid, missingScopes } = validateScopes(grantedScopes, requiredScopes); + + if (valid) { + this.authCompletedSuccessfully = true; + return true; + } + + // Token is valid but missing required scopes - need to re-authenticate + process.stderr.write(`\n${getMissingScopesMessage(missingScopes)}\n\n`); + } } // Try to start the server and get the port @@ -180,7 +201,7 @@ export class AuthServer { // Generate Auth URL using the newly created flow client const authorizeUrl = this.flowOAuth2Client.generateAuthUrl({ access_type: 'offline', - scope: ['https://www.googleapis.com/auth/calendar'], + scope: getRequiredScopes(this.enableTasks), prompt: 'consent' }); @@ -331,7 +352,7 @@ export class AuthServer { // Generate Auth URL const authUrl = this.flowOAuth2Client.generateAuthUrl({ access_type: 'offline', - scope: ['https://www.googleapis.com/auth/calendar'], + scope: getRequiredScopes(this.enableTasks), prompt: 'consent' }); diff --git a/src/auth/tokenManager.ts b/src/auth/tokenManager.ts index c23467c..96af549 100644 --- a/src/auth/tokenManager.ts +++ b/src/auth/tokenManager.ts @@ -20,6 +20,7 @@ interface CachedCredentials extends Credentials { cached_email?: string; cached_calendars?: CachedCalendar[]; calendars_cached_at?: number; + granted_scopes?: string[]; // OAuth scopes granted during authentication } // Interface for multi-account token storage @@ -350,7 +351,7 @@ export class TokenManager { } } - async saveTokens(tokens: Credentials, email?: string): Promise { + async saveTokens(tokens: Credentials, email?: string, scopes?: string[]): Promise { try { // Wrap entire read-modify-write in the queue to prevent race conditions await this.enqueueTokenWrite(async () => { @@ -362,6 +363,11 @@ export class TokenManager { cachedTokens.cached_email = email; } + // Cache the granted scopes if provided + if (scopes && scopes.length > 0) { + cachedTokens.granted_scopes = scopes; + } + multiAccountTokens[this.accountMode] = cachedTokens; await this.ensureTokenDirectoryExists(); @@ -419,6 +425,22 @@ export class TokenManager { } } + /** + * Get the granted OAuth scopes for a specific account + * @param accountId - Account ID to check (defaults to current account mode) + * @returns Array of granted scopes, or undefined if not tracked + */ + async getGrantedScopes(accountId?: string): Promise { + try { + const multiAccountTokens = await this.loadMultiAccountTokens(); + const account = accountId || this.accountMode; + const tokens = multiAccountTokens[account]; + return tokens?.granted_scopes; + } catch (error) { + return undefined; + } + } + /** * Remove a specific account's tokens from storage. * @param accountId - The account ID to remove diff --git a/src/config/TransportConfig.ts b/src/config/TransportConfig.ts index e7b8de6..432598d 100644 --- a/src/config/TransportConfig.ts +++ b/src/config/TransportConfig.ts @@ -8,6 +8,7 @@ export interface ServerConfig { transport: TransportConfig; debug?: boolean; enabledTools?: string[]; + enableTasks?: boolean; } function parseEnabledTools(value: string | undefined, source: string): string[] | undefined { @@ -33,7 +34,8 @@ export function parseArgs(args: string[]): ServerConfig { host: process.env.HOST || '127.0.0.1' }, debug: process.env.DEBUG === 'true' || false, - enabledTools: parseEnabledTools(process.env.ENABLED_TOOLS, 'ENABLED_TOOLS') + enabledTools: parseEnabledTools(process.env.ENABLED_TOOLS, 'ENABLED_TOOLS'), + enableTasks: process.env.ENABLE_TASKS === 'true' || false }; for (let i = 0; i < args.length; i++) { @@ -63,6 +65,9 @@ export function parseArgs(args: string[]): ServerConfig { } config.enabledTools = parseEnabledTools(enabledTools, '--enable-tools'); break; + case '--enable-tasks': + config.enableTasks = true; + break; case '--help': process.stderr.write(` Google Calendar MCP Server @@ -75,6 +80,7 @@ Options: --host Host for HTTP transport (default: 127.0.0.1) --debug Enable debug logging --enable-tools Comma-separated list of tools to enable (whitelist) + --enable-tasks Enable Google Tasks integration (off by default) --help Show this help message Environment Variables: @@ -83,11 +89,13 @@ Environment Variables: HOST Host for HTTP transport DEBUG Enable debug logging (true/false) ENABLED_TOOLS Comma-separated list of tools to enable + ENABLE_TASKS Enable Google Tasks integration (true/false) Examples: node build/index.js # stdio (local use) node build/index.js --transport http --port 3000 # HTTP server node build/index.js --enable-tools list-events,create-event,get-current-time + node build/index.js --enable-tasks # Enable Tasks feature PORT=3000 TRANSPORT=http node build/index.js # Using env vars `); process.exit(0); diff --git a/src/handlers/core/BaseTaskHandler.ts b/src/handlers/core/BaseTaskHandler.ts new file mode 100644 index 0000000..3917c7a --- /dev/null +++ b/src/handlers/core/BaseTaskHandler.ts @@ -0,0 +1,143 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { OAuth2Client } from "google-auth-library"; +import { tasks_v1, google } from "googleapis"; +import { BaseToolHandler } from "./BaseToolHandler.js"; +import { getCredentialsProjectId } from "../../auth/utils.js"; + +/** + * Base handler class for Google Tasks API tools. + * Extends BaseToolHandler with Tasks-specific functionality. + */ +export abstract class BaseTaskHandler extends BaseToolHandler { + /** + * Get the account ID to use for the response. + * Returns the specified account, or the single available account, or 'default'. + */ + protected getAccountId(account: string | undefined, accounts: Map): string { + return account || (accounts.size === 1 ? Array.from(accounts.keys())[0] : 'default'); + } + + /** + * Create a JSON response for MCP tools. + */ + protected jsonResponse(result: unknown): CallToolResult { + return { + content: [{ + type: "text", + text: JSON.stringify(result, null, 2) + }] + }; + } + + /** + * Normalize due date to RFC 3339 format. + * Converts date-only format (YYYY-MM-DD) to full RFC 3339. + */ + protected normalizeDueDate(due: string): string { + if (/^\d{4}-\d{2}-\d{2}$/.test(due)) { + return `${due}T00:00:00.000Z`; + } + return due; + } + + /** + * Get a Google Tasks API client instance. + * + * @param auth - OAuth2Client for authentication + * @returns Tasks API v1 client + */ + protected getTasks(auth: OAuth2Client): tasks_v1.Tasks { + // Try to get project ID from credentials file for quota project header + const quotaProjectId = getCredentialsProjectId(); + + const config: any = { + version: 'v1', + auth, + timeout: 3000 // 3 second timeout for API calls + }; + + // Add quota project ID if available + if (quotaProjectId) { + config.quotaProjectId = quotaProjectId; + } + + return google.tasks(config); + } + + /** + * Get the default task list ID for an account. + * The Google Tasks API uses '@default' to reference the user's default task list. + * + * @param taskListId - Task list ID from input, or undefined + * @returns The task list ID to use (defaults to '@default') + */ + protected getTaskListId(taskListId?: string): string { + return taskListId || '@default'; + } + + /** + * Format a task for structured response output. + * + * @param task - Raw task from Google Tasks API + * @returns Formatted task object + */ + protected formatTask(task: tasks_v1.Schema$Task): FormattedTask { + return { + id: task.id || '', + title: task.title || '', + notes: task.notes || undefined, + status: task.status as 'needsAction' | 'completed' || 'needsAction', + due: task.due || undefined, + completed: task.completed || undefined, + parent: task.parent || undefined, + position: task.position || undefined, + updated: task.updated || undefined, + selfLink: task.selfLink || undefined, + hidden: task.hidden || false, + deleted: task.deleted || false + }; + } + + /** + * Format a task list for structured response output. + * + * @param taskList - Raw task list from Google Tasks API + * @returns Formatted task list object + */ + protected formatTaskList(taskList: tasks_v1.Schema$TaskList): FormattedTaskList { + return { + id: taskList.id || '', + title: taskList.title || '', + updated: taskList.updated || undefined, + selfLink: taskList.selfLink || undefined + }; + } +} + +/** + * Formatted task structure for API responses. + */ +export interface FormattedTask { + id: string; + title: string; + notes?: string; + status: 'needsAction' | 'completed'; + due?: string; + completed?: string; + parent?: string; + position?: string; + updated?: string; + selfLink?: string; + hidden: boolean; + deleted: boolean; +} + +/** + * Formatted task list structure for API responses. + */ +export interface FormattedTaskList { + id: string; + title: string; + updated?: string; + selfLink?: string; +} diff --git a/src/handlers/core/CreateTaskHandler.ts b/src/handlers/core/CreateTaskHandler.ts new file mode 100644 index 0000000..a467c7f --- /dev/null +++ b/src/handlers/core/CreateTaskHandler.ts @@ -0,0 +1,174 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { OAuth2Client } from "google-auth-library"; +import { BaseTaskHandler } from "./BaseTaskHandler.js"; +import { CreateTaskInput } from "../../tools/task-schemas.js"; +import { CreateTaskResponse } from "../../types/task-responses.js"; + +/** + * Handler for create-task tool. + * Creates a new task in the specified task list. + * Supports recurring tasks by creating multiple individual tasks. + */ +export class CreateTaskHandler extends BaseTaskHandler { + async runTool( + args: CreateTaskInput, + accounts: Map + ): Promise { + try { + const client = this.getClientForAccount(args.account, accounts); + const tasks = this.getTasks(client); + const taskListId = this.getTaskListId(args.taskListId); + const accountId = this.getAccountId(args.account, accounts); + + if (args.recurrence) { + return await this.createRecurringTasks(tasks, taskListId, accountId, args); + } + + const task = await this.createSingleTask(tasks, taskListId, args); + + return this.jsonResponse({ + task, + taskListId, + accountId, + message: `Task "${args.title}" created successfully` + } satisfies CreateTaskResponse); + } catch (error) { + return this.handleGoogleApiError(error); + } + } + + /** + * Create a single task (helper method). + */ + private async createSingleTask( + tasks: any, + taskListId: string, + args: CreateTaskInput + ) { + const taskBody: any = { title: args.title }; + if (args.notes) taskBody.notes = args.notes; + if (args.due) taskBody.due = this.normalizeDueDate(args.due); + + const response = await tasks.tasks.insert({ + tasklist: taskListId, + ...(args.parent && { parent: args.parent }), + ...(args.previous && { previous: args.previous }), + requestBody: taskBody + }); + + if (!response.data) { + throw new Error('Failed to create task - no data returned'); + } + + return this.formatTask(response.data); + } + + /** + * Create multiple recurring tasks based on recurrence pattern. + */ + private async createRecurringTasks( + tasks: any, + taskListId: string, + accountId: string, + args: CreateTaskInput + ): Promise { + if (!args.recurrence) { + throw new Error('Recurrence pattern not provided'); + } + if (!args.due) { + throw new Error('Due date is required for recurring tasks'); + } + + const baseDueDate = this.parseDueDate(args.due); + const dueDates = this.calculateRecurringDueDates(baseDueDate, args.recurrence); + + const createdTasks = []; + for (let i = 0; i < dueDates.length; i++) { + const taskTitle = dueDates.length > 1 + ? `${args.title} (#${i + 1}/${dueDates.length})` + : args.title; + + const task = await this.createSingleTask(tasks, taskListId, { + ...args, + title: taskTitle, + due: dueDates[i].toISOString(), + recurrence: undefined + }); + createdTasks.push(task); + } + + return this.jsonResponse({ + tasks: createdTasks, + taskListId, + accountId, + message: `Created ${createdTasks.length} recurring tasks: "${args.title}"`, + recurringInfo: { + frequency: args.recurrence.frequency, + interval: args.recurrence.interval || 1, + occurrencesCreated: createdTasks.length + } + } satisfies CreateTaskResponse); + } + + /** + * Parse due date string to Date object. + */ + private parseDueDate(dueStr: string): Date { + if (/^\d{4}-\d{2}-\d{2}$/.test(dueStr)) { + return new Date(dueStr + 'T00:00:00.000Z'); + } + return new Date(dueStr); + } + + /** + * Calculate due dates for recurring tasks based on recurrence pattern. + */ + private calculateRecurringDueDates( + baseDate: Date, + recurrence: NonNullable + ): Date[] { + const dates: Date[] = []; + const { frequency, interval = 1, count, until } = recurrence; + + // Determine how many occurrences to create + let maxOccurrences = count || 365; // Default to max if no count specified + if (until) { + maxOccurrences = 365; // Will be limited by until date + } + + const untilDate = until ? new Date(until + 'T23:59:59.999Z') : null; + + for (let i = 0; i < maxOccurrences; i++) { + const date = new Date(baseDate); + + switch (frequency) { + case 'daily': + date.setUTCDate(date.getUTCDate() + (i * interval)); + break; + case 'weekly': + date.setUTCDate(date.getUTCDate() + (i * interval * 7)); + break; + case 'monthly': + date.setUTCMonth(date.getUTCMonth() + (i * interval)); + break; + case 'yearly': + date.setUTCFullYear(date.getUTCFullYear() + (i * interval)); + break; + } + + // Stop if we've passed the until date + if (untilDate && date > untilDate) { + break; + } + + dates.push(date); + } + + // If count was specified, ensure we don't exceed it + if (count && dates.length > count) { + return dates.slice(0, count); + } + + return dates; + } +} diff --git a/src/handlers/core/CreateTaskListHandler.ts b/src/handlers/core/CreateTaskListHandler.ts new file mode 100644 index 0000000..eeec624 --- /dev/null +++ b/src/handlers/core/CreateTaskListHandler.ts @@ -0,0 +1,37 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { OAuth2Client } from "google-auth-library"; +import { BaseTaskHandler } from "./BaseTaskHandler.js"; +import { CreateTaskListInput } from "../../tools/task-schemas.js"; +import { CreateTaskListResponse } from "../../types/task-responses.js"; + +/** + * Handler for create-task-list tool. + * Creates a new task list for the authenticated user. + */ +export class CreateTaskListHandler extends BaseTaskHandler { + async runTool( + args: CreateTaskListInput, + accounts: Map + ): Promise { + try { + const client = this.getClientForAccount(args.account, accounts); + const tasks = this.getTasks(client); + + const response = await tasks.tasklists.insert({ + requestBody: { title: args.title } + }); + + if (!response.data) { + throw new Error('Failed to create task list - no data returned'); + } + + return this.jsonResponse({ + taskList: this.formatTaskList(response.data), + accountId: this.getAccountId(args.account, accounts), + message: `Task list "${args.title}" created successfully` + } satisfies CreateTaskListResponse); + } catch (error) { + return this.handleGoogleApiError(error); + } + } +} diff --git a/src/handlers/core/DeleteTaskHandler.ts b/src/handlers/core/DeleteTaskHandler.ts new file mode 100644 index 0000000..ce6d2e7 --- /dev/null +++ b/src/handlers/core/DeleteTaskHandler.ts @@ -0,0 +1,43 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { OAuth2Client } from "google-auth-library"; +import { BaseTaskHandler } from "./BaseTaskHandler.js"; +import { DeleteTaskInput } from "../../tools/task-schemas.js"; +import { DeleteTaskResponse } from "../../types/task-responses.js"; + +/** + * Handler for delete-task tool. + * Permanently deletes a task from the specified task list. + */ +export class DeleteTaskHandler extends BaseTaskHandler { + async runTool( + args: DeleteTaskInput, + accounts: Map + ): Promise { + try { + const client = this.getClientForAccount(args.account, accounts); + const tasks = this.getTasks(client); + const taskListId = this.getTaskListId(args.taskListId); + + // Get task title for confirmation message + let taskTitle = args.taskId; + try { + const existingTask = await tasks.tasks.get({ tasklist: taskListId, task: args.taskId }); + if (existingTask.data?.title) taskTitle = existingTask.data.title; + } catch { + // If we can't get the task info, proceed with deletion anyway + } + + await tasks.tasks.delete({ tasklist: taskListId, task: args.taskId }); + + return this.jsonResponse({ + success: true, + taskId: args.taskId, + taskListId, + accountId: this.getAccountId(args.account, accounts), + message: `Task "${taskTitle}" deleted successfully` + } satisfies DeleteTaskResponse); + } catch (error) { + return this.handleGoogleApiError(error); + } + } +} diff --git a/src/handlers/core/GetTaskHandler.ts b/src/handlers/core/GetTaskHandler.ts new file mode 100644 index 0000000..b742356 --- /dev/null +++ b/src/handlers/core/GetTaskHandler.ts @@ -0,0 +1,39 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { OAuth2Client } from "google-auth-library"; +import { BaseTaskHandler } from "./BaseTaskHandler.js"; +import { GetTaskInput } from "../../tools/task-schemas.js"; +import { GetTaskResponse } from "../../types/task-responses.js"; + +/** + * Handler for get-task tool. + * Gets details of a specific task by ID. + */ +export class GetTaskHandler extends BaseTaskHandler { + async runTool( + args: GetTaskInput, + accounts: Map + ): Promise { + try { + const client = this.getClientForAccountOrFirst(args.account, accounts); + const tasks = this.getTasks(client); + const taskListId = this.getTaskListId(args.taskListId); + + const response = await tasks.tasks.get({ + tasklist: taskListId, + task: args.taskId + }); + + if (!response.data) { + throw new Error(`Task not found: ${args.taskId}`); + } + + return this.jsonResponse({ + task: this.formatTask(response.data), + taskListId, + accountId: this.getAccountId(args.account, accounts) + } satisfies GetTaskResponse); + } catch (error) { + return this.handleGoogleApiError(error); + } + } +} diff --git a/src/handlers/core/ListTaskListsHandler.ts b/src/handlers/core/ListTaskListsHandler.ts new file mode 100644 index 0000000..7d1533c --- /dev/null +++ b/src/handlers/core/ListTaskListsHandler.ts @@ -0,0 +1,32 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { OAuth2Client } from "google-auth-library"; +import { BaseTaskHandler } from "./BaseTaskHandler.js"; +import { ListTaskListsInput } from "../../tools/task-schemas.js"; +import { ListTaskListsResponse } from "../../types/task-responses.js"; + +/** + * Handler for list-task-lists tool. + * Lists all task lists for the authenticated user. + */ +export class ListTaskListsHandler extends BaseTaskHandler { + async runTool( + args: ListTaskListsInput, + accounts: Map + ): Promise { + try { + const client = this.getClientForAccountOrFirst(args.account, accounts); + const tasks = this.getTasks(client); + + const response = await tasks.tasklists.list({ maxResults: 100 }); + const taskLists = (response.data.items || []).map(tl => this.formatTaskList(tl)); + + return this.jsonResponse({ + taskLists, + totalCount: taskLists.length, + accountId: this.getAccountId(args.account, accounts) + } satisfies ListTaskListsResponse); + } catch (error) { + return this.handleGoogleApiError(error); + } + } +} diff --git a/src/handlers/core/ListTasksHandler.ts b/src/handlers/core/ListTasksHandler.ts new file mode 100644 index 0000000..648abab --- /dev/null +++ b/src/handlers/core/ListTasksHandler.ts @@ -0,0 +1,58 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { OAuth2Client } from "google-auth-library"; +import { BaseTaskHandler } from "./BaseTaskHandler.js"; +import { ListTasksInput } from "../../tools/task-schemas.js"; +import { ListTasksResponse } from "../../types/task-responses.js"; + +/** + * Handler for list-tasks tool. + * Lists tasks within a specific task list with optional filtering. + */ +export class ListTasksHandler extends BaseTaskHandler { + async runTool( + args: ListTasksInput, + accounts: Map + ): Promise { + try { + const client = this.getClientForAccountOrFirst(args.account, accounts); + const tasks = this.getTasks(client); + const taskListId = this.getTaskListId(args.taskListId); + + const response = await tasks.tasks.list({ + tasklist: taskListId, + maxResults: args.maxResults || 100, + showCompleted: args.showCompleted ?? true, + showHidden: args.showHidden ?? false, + ...(args.dueMin && { dueMin: args.dueMin }), + ...(args.dueMax && { dueMax: args.dueMax }) + }); + + const formattedTasks = (response.data.items || []).map(t => this.formatTask(t)); + + // Get task list title + let taskListTitle: string | undefined; + try { + const listInfo = await tasks.tasklists.get({ tasklist: taskListId }); + taskListTitle = listInfo.data.title || undefined; + } catch { + // Ignore errors getting task list title + } + + return this.jsonResponse({ + tasks: formattedTasks, + totalCount: formattedTasks.length, + taskListId, + taskListTitle, + accountId: this.getAccountId(args.account, accounts), + filters: { + showCompleted: args.showCompleted ?? true, + showHidden: args.showHidden ?? false, + dueMin: args.dueMin, + dueMax: args.dueMax + } + } satisfies ListTasksResponse); + } catch (error) { + return this.handleGoogleApiError(error); + } + } +} diff --git a/src/handlers/core/UpdateTaskHandler.ts b/src/handlers/core/UpdateTaskHandler.ts new file mode 100644 index 0000000..52f32b7 --- /dev/null +++ b/src/handlers/core/UpdateTaskHandler.ts @@ -0,0 +1,79 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { OAuth2Client } from "google-auth-library"; +import { BaseTaskHandler } from "./BaseTaskHandler.js"; +import { UpdateTaskInput } from "../../tools/task-schemas.js"; +import { UpdateTaskResponse } from "../../types/task-responses.js"; + +/** + * Handler for update-task tool. + * Updates an existing task with new values. + */ +export class UpdateTaskHandler extends BaseTaskHandler { + async runTool( + args: UpdateTaskInput, + accounts: Map + ): Promise { + try { + const client = this.getClientForAccount(args.account, accounts); + const tasks = this.getTasks(client); + const taskListId = this.getTaskListId(args.taskListId); + + // Get existing task to merge with updates + const existingTask = await tasks.tasks.get({ + tasklist: taskListId, + task: args.taskId + }); + + if (!existingTask.data) { + throw new Error(`Task not found: ${args.taskId}`); + } + + const updatedFields: string[] = []; + const taskBody: any = { + id: existingTask.data.id, + title: existingTask.data.title, + notes: existingTask.data.notes, + status: existingTask.data.status, + due: existingTask.data.due + }; + + if (args.title !== undefined) { + taskBody.title = args.title; + updatedFields.push('title'); + } + if (args.notes !== undefined) { + taskBody.notes = args.notes; + updatedFields.push('notes'); + } + if (args.due !== undefined) { + taskBody.due = this.normalizeDueDate(args.due); + updatedFields.push('due'); + } + if (args.status !== undefined) { + taskBody.status = args.status; + taskBody.completed = args.status === 'completed' ? new Date().toISOString() : null; + updatedFields.push('status'); + } + + const response = await tasks.tasks.update({ + tasklist: taskListId, + task: args.taskId, + requestBody: taskBody + }); + + if (!response.data) { + throw new Error('Failed to update task - no data returned'); + } + + return this.jsonResponse({ + task: this.formatTask(response.data), + taskListId, + accountId: this.getAccountId(args.account, accounts), + message: `Task updated successfully`, + updatedFields + } satisfies UpdateTaskResponse); + } catch (error) { + return this.handleGoogleApiError(error); + } + } +} diff --git a/src/index.ts b/src/index.ts index a7b66a0..792f002 100644 --- a/src/index.ts +++ b/src/index.ts @@ -107,6 +107,7 @@ Commands: Options: --enable-tools Comma-separated list of tools to enable (whitelist) + --enable-tasks Enable Google Tasks integration (requires Tasks API enabled) Examples: npx @cocal/google-calendar-mcp auth # Authenticate default account @@ -119,6 +120,7 @@ Environment Variables: GOOGLE_OAUTH_CREDENTIALS Path to OAuth credentials file GOOGLE_ACCOUNT_MODE Account ID to use (alternative to auth argument) ENABLED_TOOLS Comma-separated list of tools to enable + ENABLE_TASKS Set to 'true' to enable Google Tasks integration `); } diff --git a/src/server.ts b/src/server.ts index 5ae7a03..40ffc8a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -41,7 +41,7 @@ export class GoogleCalendarMcpServer { // 1. Initialize Authentication (but don't block on it) this.oauth2Client = await initializeOAuth2Client(); this.tokenManager = new TokenManager(this.oauth2Client); - this.authServer = new AuthServer(this.oauth2Client); + this.authServer = new AuthServer(this.oauth2Client, this.config.enableTasks); // 2. Load all authenticated accounts this.accounts = await this.tokenManager.loadAllAccounts(); diff --git a/src/tests/integration/tasks-integration.test.ts b/src/tests/integration/tasks-integration.test.ts new file mode 100644 index 0000000..b2c9932 --- /dev/null +++ b/src/tests/integration/tasks-integration.test.ts @@ -0,0 +1,764 @@ +import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'vitest'; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; +import { ChildProcess } from 'child_process'; + +/** + * Integration Tests for Google Tasks MCP Tools + * + * REQUIREMENTS TO RUN THESE TESTS: + * 1. Valid Google OAuth credentials file at path specified by GOOGLE_OAUTH_CREDENTIALS env var + * 2. Authenticated test account with Tasks scope: Run `ENABLE_TASKS=true npm run dev auth:test` first + * 3. ENABLE_TASKS=true environment variable set + * 4. Google Tasks API enabled in Google Cloud Console + * 5. Network access to Google Tasks API + * + * These tests exercise all Tasks MCP tools against real Google Tasks and will: + * - Create, modify, and delete real tasks + * - Make actual API calls to Google Tasks API + * - Require valid authentication tokens with Tasks scope + * + * Test Strategy: + * 1. Verify task lists can be retrieved + * 2. Create test tasks + * 3. Test read operations (get, list) + * 4. Test write operations (update, complete via update) + * 5. Clean up by deleting created tasks + */ + +describe('Google Tasks MCP - Direct Integration Tests', () => { + let client: Client; + let serverProcess: ChildProcess; + let createdTaskIds: string[] = []; + let createdTaskListIds: string[] = []; + const defaultTaskListId: string = '@default'; + // Use test-primary account which should have Tasks scope + const TEST_ACCOUNT = process.env.TEST_ACCOUNT_ID || 'test-primary'; + + // Performance tracking + const performanceMetrics: Map = new Map(); + + const startTimer = (operation: string): number => { + return Date.now(); + }; + + const endTimer = (operation: string, startTime: number, success: boolean, error?: string): void => { + const duration = Date.now() - startTime; + if (!performanceMetrics.has(operation)) { + performanceMetrics.set(operation, []); + } + performanceMetrics.get(operation)!.push({ duration, success, error }); + }; + + const logPerformanceSummary = (): void => { + console.log('\n📊 Performance Summary:'); + for (const [operation, metrics] of performanceMetrics) { + const successful = metrics.filter(m => m.success); + const avgDuration = successful.length > 0 + ? Math.round(successful.reduce((sum, m) => sum + m.duration, 0) / successful.length) + : 0; + console.log(` ${operation}: ${successful.length}/${metrics.length} successful, avg ${avgDuration}ms`); + } + }; + + beforeAll(async () => { + // Verify ENABLE_TASKS is set + if (process.env.ENABLE_TASKS !== 'true') { + console.warn('⚠️ ENABLE_TASKS not set to true - Tasks integration tests may fail'); + } + + console.log('🚀 Starting Google Calendar MCP server with Tasks enabled...'); + + // Filter out undefined values from process.env and set required vars + const cleanEnv = Object.fromEntries( + Object.entries(process.env).filter(([_, value]) => value !== undefined) + ) as Record; + cleanEnv.NODE_ENV = 'test'; + cleanEnv.ENABLE_TASKS = 'true'; + + // Create MCP client + client = new Client({ + name: "tasks-integration-test-client", + version: "1.0.0" + }, { + capabilities: { + tools: {} + } + }); + + // Connect to server + const transport = new StdioClientTransport({ + command: 'node', + args: ['build/index.js', '--enable-tasks'], + env: cleanEnv + }); + + await client.connect(transport); + console.log('✅ Connected to MCP server with Tasks enabled'); + }, 30000); + + afterAll(async () => { + console.log('\n🏁 Starting final cleanup...'); + + // Final cleanup - ensure all test tasks are removed + if (createdTaskIds.length > 0) { + console.log(`📋 Cleaning up ${createdTaskIds.length} tasks created during tests`); + for (const taskId of createdTaskIds) { + try { + await client.callTool({ + name: 'delete-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: defaultTaskListId, + taskId: taskId + } + }); + } catch (error) { + console.warn(`Failed to delete task ${taskId}: ${error}`); + } + } + } + + // Clean up any created task lists (except default) + if (createdTaskListIds.length > 0) { + console.log(`📋 Cleaning up ${createdTaskListIds.length} task lists created during tests`); + for (const taskListId of createdTaskListIds) { + try { + // Note: Google Tasks API doesn't have a delete task list endpoint via MCP + // Task lists created during tests will remain but are harmless + console.log(` (Task list ${taskListId} will persist - no delete API available)`); + } catch (error) { + console.warn(`Failed to clean up task list ${taskListId}: ${error}`); + } + } + } + + // Close client connection + if (client) { + await client.close(); + console.log('🔌 Closed MCP client connection'); + } + + // Terminate server process + if (serverProcess && !serverProcess.killed) { + serverProcess.kill(); + await new Promise(resolve => setTimeout(resolve, 1000)); + console.log('🛑 Terminated MCP server process'); + } + + // Log performance summary + logPerformanceSummary(); + + console.log('✅ Tasks integration test cleanup completed successfully\n'); + }, 30000); + + beforeEach(() => { + performanceMetrics.clear(); + }); + + afterEach(async () => { + // Cleanup tasks created in this test + if (createdTaskIds.length > 0) { + console.log(`🧹 Cleaning up ${createdTaskIds.length} tasks from test...`); + for (const taskId of createdTaskIds) { + try { + await client.callTool({ + name: 'delete-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: defaultTaskListId, + taskId: taskId + } + }); + } catch (error) { + // Ignore cleanup errors + } + } + createdTaskIds = []; + } + }); + + describe('Task List Operations', () => { + it('should list task lists', async () => { + const startTime = startTimer('list-task-lists'); + + try { + const result = await client.callTool({ + name: 'list-task-lists', + arguments: { + account: TEST_ACCOUNT + } + }); + + endTimer('list-task-lists', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.taskLists).toBeDefined(); + expect(Array.isArray(response.taskLists)).toBe(true); + expect(response.totalCount).toBeTypeOf('number'); + expect(response.totalCount).toBeGreaterThan(0); + + // Verify at least one task list has required fields + const firstList = response.taskLists[0]; + expect(firstList.id).toBeDefined(); + expect(firstList.title).toBeDefined(); + } catch (error) { + endTimer('list-task-lists', startTime, false, String(error)); + throw error; + } + }); + + it('should create a new task list', async () => { + const startTime = startTimer('create-task-list'); + const uniqueTitle = `Integration Test List - ${Date.now()}`; + + try { + const result = await client.callTool({ + name: 'create-task-list', + arguments: { + account: TEST_ACCOUNT, + title: uniqueTitle + } + }); + + endTimer('create-task-list', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.taskList).toBeDefined(); + expect(response.taskList.id).toBeDefined(); + expect(response.taskList.title).toBe(uniqueTitle); + expect(response.accountId).toBeDefined(); + expect(response.message).toContain('created successfully'); + + // Track for reference (can't delete via API, but good to know it was created) + createdTaskListIds.push(response.taskList.id); + + // Verify the new list appears in list-task-lists + const listResult = await client.callTool({ + name: 'list-task-lists', + arguments: { account: TEST_ACCOUNT } + }); + const listResponse = JSON.parse((listResult.content as any)[0].text); + const foundList = listResponse.taskLists.find((tl: any) => tl.id === response.taskList.id); + expect(foundList).toBeDefined(); + expect(foundList.title).toBe(uniqueTitle); + } catch (error) { + endTimer('create-task-list', startTime, false, String(error)); + throw error; + } + }); + }); + + describe('Task CRUD Operations', () => { + it('should create a task with title only', async () => { + const startTime = startTimer('create-task-basic'); + + try { + const result = await client.callTool({ + name: 'create-task', + arguments: { + account: TEST_ACCOUNT, + title: 'Integration Test Task - Basic' + } + }); + + endTimer('create-task-basic', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.task).toBeDefined(); + expect(response.task.id).toBeDefined(); + expect(response.task.title).toBe('Integration Test Task - Basic'); + expect(response.task.status).toBe('needsAction'); + expect(response.message).toContain('created successfully'); + + // Track for cleanup + createdTaskIds.push(response.task.id); + } catch (error) { + endTimer('create-task-basic', startTime, false, String(error)); + throw error; + } + }); + + it('should create a task with all fields', async () => { + const startTime = startTimer('create-task-full'); + const dueDate = new Date(); + dueDate.setDate(dueDate.getDate() + 7); // Due in 7 days + const dueDateStr = dueDate.toISOString().split('T')[0]; + + try { + const result = await client.callTool({ + name: 'create-task', + arguments: { + account: TEST_ACCOUNT, + title: 'Integration Test Task - Full', + notes: 'This is a test task with all fields populated', + due: dueDateStr + } + }); + + endTimer('create-task-full', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.task).toBeDefined(); + expect(response.task.id).toBeDefined(); + expect(response.task.title).toBe('Integration Test Task - Full'); + expect(response.task.notes).toBe('This is a test task with all fields populated'); + expect(response.task.due).toBeDefined(); + expect(response.task.status).toBe('needsAction'); + + // Track for cleanup + createdTaskIds.push(response.task.id); + } catch (error) { + endTimer('create-task-full', startTime, false, String(error)); + throw error; + } + }); + + it('should create recurring tasks', async () => { + const startTime = startTimer('create-task-recurring'); + const dueDate = new Date(); + dueDate.setDate(dueDate.getDate() + 1); // Start tomorrow + const dueDateStr = dueDate.toISOString().split('T')[0]; + + try { + const result = await client.callTool({ + name: 'create-task', + arguments: { + account: TEST_ACCOUNT, + title: 'Integration Test Task - Recurring', + notes: 'This task recurs daily for 3 days', + due: dueDateStr, + recurrence: { + frequency: 'daily', + interval: 1, + count: 3 + } + } + }); + + endTimer('create-task-recurring', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + + // Should return multiple tasks + expect(response.tasks).toBeDefined(); + expect(Array.isArray(response.tasks)).toBe(true); + expect(response.tasks.length).toBe(3); + + // Verify recurring info + expect(response.recurringInfo).toBeDefined(); + expect(response.recurringInfo.frequency).toBe('daily'); + expect(response.recurringInfo.interval).toBe(1); + expect(response.recurringInfo.occurrencesCreated).toBe(3); + + // Verify task titles include occurrence numbers + expect(response.tasks[0].title).toContain('#1/3'); + expect(response.tasks[1].title).toContain('#2/3'); + expect(response.tasks[2].title).toContain('#3/3'); + + // Verify each task has correct structure + for (const task of response.tasks) { + expect(task.id).toBeDefined(); + expect(task.status).toBe('needsAction'); + expect(task.due).toBeDefined(); + // Track for cleanup + createdTaskIds.push(task.id); + } + + // Verify message mentions all created tasks + expect(response.message).toContain('Created 3 recurring tasks'); + } catch (error) { + endTimer('create-task-recurring', startTime, false, String(error)); + throw error; + } + }); + + it('should get a specific task by ID', async () => { + // First create a task + const createResult = await client.callTool({ + name: 'create-task', + arguments: { + account: TEST_ACCOUNT, + title: 'Integration Test Task - Get', + notes: 'Task for testing get operation' + } + }); + const createResponse = JSON.parse((createResult.content as any)[0].text); + const taskId = createResponse.task.id; + createdTaskIds.push(taskId); + + const startTime = startTimer('get-task'); + + try { + const result = await client.callTool({ + name: 'get-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default', + taskId: taskId + } + }); + + endTimer('get-task', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.task).toBeDefined(); + expect(response.task.id).toBe(taskId); + expect(response.task.title).toBe('Integration Test Task - Get'); + expect(response.task.notes).toBe('Task for testing get operation'); + } catch (error) { + endTimer('get-task', startTime, false, String(error)); + throw error; + } + }); + + it('should list tasks in default task list', async () => { + // First create a task to ensure there's at least one + const createResult = await client.callTool({ + name: 'create-task', + arguments: { + account: TEST_ACCOUNT, + title: 'Integration Test Task - List' + } + }); + const createResponse = JSON.parse((createResult.content as any)[0].text); + createdTaskIds.push(createResponse.task.id); + + const startTime = startTimer('list-tasks'); + + try { + const result = await client.callTool({ + name: 'list-tasks', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default' + } + }); + + endTimer('list-tasks', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.tasks).toBeDefined(); + expect(Array.isArray(response.tasks)).toBe(true); + expect(response.totalCount).toBeTypeOf('number'); + expect(response.taskListId).toBe('@default'); + + // Verify our created task is in the list + const foundTask = response.tasks.find((t: any) => t.title === 'Integration Test Task - List'); + expect(foundTask).toBeDefined(); + } catch (error) { + endTimer('list-tasks', startTime, false, String(error)); + throw error; + } + }); + + it('should list tasks with showCompleted filter', async () => { + const startTime = startTimer('list-tasks-filter'); + + try { + const result = await client.callTool({ + name: 'list-tasks', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default', + showCompleted: false + } + }); + + endTimer('list-tasks-filter', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.tasks).toBeDefined(); + expect(response.filters).toBeDefined(); + expect(response.filters.showCompleted).toBe(false); + + // All returned tasks should be incomplete + for (const task of response.tasks) { + expect(task.status).toBe('needsAction'); + } + } catch (error) { + endTimer('list-tasks-filter', startTime, false, String(error)); + throw error; + } + }); + + it('should update a task title and notes', async () => { + // First create a task + const createResult = await client.callTool({ + name: 'create-task', + arguments: { + account: TEST_ACCOUNT, + title: 'Integration Test Task - Update Original' + } + }); + const createResponse = JSON.parse((createResult.content as any)[0].text); + const taskId = createResponse.task.id; + createdTaskIds.push(taskId); + + const startTime = startTimer('update-task'); + + try { + const result = await client.callTool({ + name: 'update-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default', + taskId: taskId, + title: 'Integration Test Task - Updated', + notes: 'These notes were added during update' + } + }); + + endTimer('update-task', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.task).toBeDefined(); + expect(response.task.id).toBe(taskId); + expect(response.task.title).toBe('Integration Test Task - Updated'); + expect(response.task.notes).toBe('These notes were added during update'); + expect(response.message).toContain('updated successfully'); + expect(response.updatedFields).toContain('title'); + expect(response.updatedFields).toContain('notes'); + } catch (error) { + endTimer('update-task', startTime, false, String(error)); + throw error; + } + }); + + it('should complete a task via update-task with status', async () => { + // First create a task + const createResult = await client.callTool({ + name: 'create-task', + arguments: { + account: TEST_ACCOUNT, + title: 'Integration Test Task - Complete via Update' + } + }); + const createResponse = JSON.parse((createResult.content as any)[0].text); + const taskId = createResponse.task.id; + createdTaskIds.push(taskId); + + const startTime = startTimer('update-task-complete'); + + try { + const result = await client.callTool({ + name: 'update-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default', + taskId: taskId, + status: 'completed' + } + }); + + endTimer('update-task-complete', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.task).toBeDefined(); + expect(response.task.id).toBe(taskId); + expect(response.task.status).toBe('completed'); + expect(response.task.completed).toBeDefined(); + expect(response.updatedFields).toContain('status'); + } catch (error) { + endTimer('update-task-complete', startTime, false, String(error)); + throw error; + } + }); + + it('should uncomplete a task via update-task with status needsAction', async () => { + // First create and complete a task + const createResult = await client.callTool({ + name: 'create-task', + arguments: { + account: TEST_ACCOUNT, + title: 'Integration Test Task - Uncomplete' + } + }); + const createResponse = JSON.parse((createResult.content as any)[0].text); + const taskId = createResponse.task.id; + createdTaskIds.push(taskId); + + // Complete it first + await client.callTool({ + name: 'update-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default', + taskId: taskId, + status: 'completed' + } + }); + + const startTime = startTimer('update-task-uncomplete'); + + try { + // Now uncomplete it + const result = await client.callTool({ + name: 'update-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default', + taskId: taskId, + status: 'needsAction' + } + }); + + endTimer('update-task-uncomplete', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.task).toBeDefined(); + expect(response.task.id).toBe(taskId); + expect(response.task.status).toBe('needsAction'); + expect(response.updatedFields).toContain('status'); + } catch (error) { + endTimer('update-task-uncomplete', startTime, false, String(error)); + throw error; + } + }); + + it('should delete a task', async () => { + // First create a task + const createResult = await client.callTool({ + name: 'create-task', + arguments: { + account: TEST_ACCOUNT, + title: 'Integration Test Task - Delete' + } + }); + const createResponse = JSON.parse((createResult.content as any)[0].text); + const taskId = createResponse.task.id; + // Don't add to createdTaskIds since we're deleting it in the test + + const startTime = startTimer('delete-task'); + + try { + const result = await client.callTool({ + name: 'delete-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default', + taskId: taskId + } + }); + + endTimer('delete-task', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.success).toBe(true); + expect(response.taskId).toBe(taskId); + expect(response.message).toContain('deleted successfully'); + + // Verify task is actually deleted by trying to get it + const getResult = await client.callTool({ + name: 'get-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default', + taskId: taskId + } + }); + const getResponse = JSON.parse((getResult.content as any)[0].text); + // Should return an error or empty result + expect(getResponse.error || getResponse.task?.deleted).toBeTruthy(); + } catch (error) { + endTimer('delete-task', startTime, false, String(error)); + throw error; + } + }); + }); + + describe('Error Handling', () => { + it('should handle non-existent task gracefully', async () => { + const startTime = startTimer('get-task-not-found'); + + try { + const result = await client.callTool({ + name: 'get-task', + arguments: { + account: TEST_ACCOUNT, + taskListId: '@default', + taskId: 'nonexistent-task-id-12345' + } + }); + + endTimer('get-task-not-found', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + // Should return an error response + expect(response.error || response.message?.toLowerCase().includes('not found')).toBeTruthy(); + } catch (error) { + // Error is also acceptable for not found + endTimer('get-task-not-found', startTime, true); + expect(String(error)).toMatch(/not found|404|invalid|MCP error/i); + } + }); + + it('should handle invalid task list ID gracefully', async () => { + const startTime = startTimer('list-tasks-invalid-list'); + + try { + const result = await client.callTool({ + name: 'list-tasks', + arguments: { + account: TEST_ACCOUNT, + taskListId: 'invalid-task-list-id-99999' + } + }); + + endTimer('list-tasks-invalid-list', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + // Should return an error response + expect(response.error || response.message?.toLowerCase().includes('not found')).toBeTruthy(); + } catch (error) { + // Error is also acceptable + endTimer('list-tasks-invalid-list', startTime, true); + expect(String(error)).toMatch(/not found|404|invalid|MCP error/i); + } + }); + }); + + describe('Multi-Account Support', () => { + it('should support account parameter in list-task-lists', async () => { + const startTime = startTimer('list-task-lists-account'); + + try { + // Use test-primary account if available + const result = await client.callTool({ + name: 'list-task-lists', + arguments: { + account: 'test-primary' + } + }); + + endTimer('list-task-lists-account', startTime, true); + + expect(result.content).toBeDefined(); + const response = JSON.parse((result.content as any)[0].text); + expect(response.taskLists).toBeDefined(); + expect(response.accountId).toBe('test-primary'); + } catch (error) { + // If test-primary account doesn't exist or doesn't have Tasks scope, that's OK + endTimer('list-task-lists-account', startTime, true); + const errorStr = String(error); + if (!errorStr.includes('not found') && !errorStr.includes('scope')) { + throw error; + } + } + }); + }); +}); diff --git a/src/tests/unit/handlers/CreateTaskHandler.test.ts b/src/tests/unit/handlers/CreateTaskHandler.test.ts new file mode 100644 index 0000000..391eda4 --- /dev/null +++ b/src/tests/unit/handlers/CreateTaskHandler.test.ts @@ -0,0 +1,346 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { OAuth2Client } from 'google-auth-library'; +import { CreateTaskHandler } from '../../../handlers/core/CreateTaskHandler.js'; + +// Mock tasks.insert function +const mockTasksInsert = vi.fn(); + +// Mock googleapis +vi.mock('googleapis', () => ({ + google: { + tasks: () => ({ + tasks: { + insert: mockTasksInsert + } + }) + } +})); + +describe('CreateTaskHandler', () => { + let handler: CreateTaskHandler; + let mockAccounts: Map; + let mockClient: OAuth2Client; + + beforeEach(() => { + vi.clearAllMocks(); + + handler = new CreateTaskHandler(); + mockClient = { + credentials: { access_token: 'test-token' } + } as unknown as OAuth2Client; + + mockAccounts = new Map([['work', mockClient]]); + }); + + it('should create a task successfully', async () => { + const mockTask = { + data: { + id: 'new-task-id', + title: 'Buy groceries', + status: 'needsAction', + notes: 'Milk, eggs, bread', + due: '2024-01-20T00:00:00Z' + } + }; + + mockTasksInsert.mockResolvedValue(mockTask); + + const result = await handler.runTool({ + account: 'work', + title: 'Buy groceries', + notes: 'Milk, eggs, bread', + due: '2024-01-20T00:00:00Z' + }, mockAccounts); + + expect(result.content).toHaveLength(1); + expect(result.content[0].type).toBe('text'); + + const response = JSON.parse(result.content[0].text); + expect(response.task.id).toBe('new-task-id'); + expect(response.task.title).toBe('Buy groceries'); + expect(response.task.notes).toBe('Milk, eggs, bread'); + expect(response.accountId).toBe('work'); + }); + + it('should use @default task list when no taskListId specified', async () => { + mockTasksInsert.mockResolvedValue({ + data: { id: 'task1', title: 'Test Task' } + }); + + await handler.runTool({ account: 'work', title: 'Test Task' }, mockAccounts); + + expect(mockTasksInsert).toHaveBeenCalledWith( + expect.objectContaining({ + tasklist: '@default' + }) + ); + }); + + it('should use specified taskListId', async () => { + mockTasksInsert.mockResolvedValue({ + data: { id: 'task1', title: 'Test Task' } + }); + + await handler.runTool({ + account: 'work', + title: 'Test Task', + taskListId: 'custom-list' + }, mockAccounts); + + expect(mockTasksInsert).toHaveBeenCalledWith( + expect.objectContaining({ + tasklist: 'custom-list' + }) + ); + }); + + it('should pass title, notes, and due in request body', async () => { + mockTasksInsert.mockResolvedValue({ + data: { id: 'task1', title: 'Test Task' } + }); + + await handler.runTool({ + account: 'work', + title: 'My Task', + notes: 'Task notes', + due: '2024-02-01T00:00:00Z' + }, mockAccounts); + + expect(mockTasksInsert).toHaveBeenCalledWith( + expect.objectContaining({ + requestBody: { + title: 'My Task', + notes: 'Task notes', + due: '2024-02-01T00:00:00Z' + } + }) + ); + }); + + it('should create task with only title', async () => { + mockTasksInsert.mockResolvedValue({ + data: { id: 'task1', title: 'Simple Task', status: 'needsAction' } + }); + + const result = await handler.runTool({ + account: 'work', + title: 'Simple Task' + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.task.title).toBe('Simple Task'); + }); + + it('should throw error when creation fails', async () => { + mockTasksInsert.mockResolvedValue({ data: null }); + + await expect( + handler.runTool({ account: 'work', title: 'Test Task' }, mockAccounts) + ).rejects.toThrow('Failed to create task - no data returned'); + }); + + it('should throw error for invalid account', async () => { + await expect( + handler.runTool({ account: 'nonexistent', title: 'Test Task' }, mockAccounts) + ).rejects.toThrow('Account "nonexistent" not found'); + }); + + describe('recurring tasks', () => { + it('should create daily recurring tasks with count', async () => { + mockTasksInsert.mockImplementation((params: any) => ({ + data: { + id: `task-${Date.now()}`, + title: params.requestBody.title, // Return the title that was passed in + status: 'needsAction' + } + })); + + const result = await handler.runTool({ + account: 'work', + title: 'Daily Task', + due: '2024-01-15', + recurrence: { + frequency: 'daily', + interval: 1, + count: 5 + } + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.tasks).toHaveLength(5); + expect(response.recurringInfo).toEqual({ + frequency: 'daily', + interval: 1, + occurrencesCreated: 5 + }); + expect(mockTasksInsert).toHaveBeenCalledTimes(5); + + // Check that titles are numbered + expect(response.tasks[0].title).toContain('#1/5'); + expect(response.tasks[4].title).toContain('#5/5'); + }); + + it('should create weekly recurring tasks', async () => { + mockTasksInsert.mockImplementation((params: any) => ({ + data: { + id: `task-${Date.now()}`, + title: params.requestBody.title, + status: 'needsAction' + } + })); + + const result = await handler.runTool({ + account: 'work', + title: 'Weekly Meeting', + due: '2024-01-15T00:00:00Z', + recurrence: { + frequency: 'weekly', + interval: 1, + count: 3 + } + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.tasks).toHaveLength(3); + expect(mockTasksInsert).toHaveBeenCalledTimes(3); + }); + + it('should create monthly recurring tasks', async () => { + mockTasksInsert.mockImplementation((params: any) => ({ + data: { + id: `task-${Date.now()}`, + title: params.requestBody.title, + status: 'needsAction' + } + })); + + const result = await handler.runTool({ + account: 'work', + title: 'Monthly Report', + due: '2024-01-01', + recurrence: { + frequency: 'monthly', + interval: 1, + count: 3 + } + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.tasks).toHaveLength(3); + }); + + it('should create yearly recurring tasks', async () => { + mockTasksInsert.mockImplementation((params: any) => ({ + data: { + id: `task-${Date.now()}`, + title: params.requestBody.title, + status: 'needsAction' + } + })); + + const result = await handler.runTool({ + account: 'work', + title: 'Annual Review', + due: '2024-12-31', + recurrence: { + frequency: 'yearly', + interval: 1, + count: 3 + } + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.tasks).toHaveLength(3); + }); + + it('should create recurring tasks with until date', async () => { + mockTasksInsert.mockImplementation((params: any) => ({ + data: { + id: `task-${Date.now()}`, + title: params.requestBody.title, + status: 'needsAction' + } + })); + + const result = await handler.runTool({ + account: 'work', + title: 'Limited Task', + due: '2024-01-01', + recurrence: { + frequency: 'weekly', + interval: 1, + until: '2024-01-21' // 3 weeks + } + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + // Should create tasks for Jan 1, 8, 15 (3 tasks, Jan 22 is beyond until date) + expect(response.tasks.length).toBeGreaterThan(0); + expect(response.tasks.length).toBeLessThanOrEqual(3); + }); + + it('should create recurring tasks with custom interval', async () => { + mockTasksInsert.mockImplementation((params: any) => ({ + data: { + id: `task-${Date.now()}`, + title: params.requestBody.title, + status: 'needsAction' + } + })); + + const result = await handler.runTool({ + account: 'work', + title: 'Every 2 Weeks', + due: '2024-01-01', + recurrence: { + frequency: 'weekly', + interval: 2, + count: 3 + } + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.tasks).toHaveLength(3); + expect(response.recurringInfo.interval).toBe(2); + }); + + it('should throw error for recurring tasks without due date', async () => { + await expect( + handler.runTool({ + account: 'work', + title: 'Task', + recurrence: { + frequency: 'daily', + interval: 1, + count: 3 + } + }, mockAccounts) + ).rejects.toThrow('Due date is required for recurring tasks'); + }); + + it('should handle date-only format in recurring tasks', async () => { + mockTasksInsert.mockImplementation((params: any) => ({ + data: { + id: `task-${Date.now()}`, + title: params.requestBody.title, + status: 'needsAction', + due: '2024-01-15T00:00:00.000Z' + } + })); + + const result = await handler.runTool({ + account: 'work', + title: 'Date Only Task', + due: '2024-01-15', // Date-only format + recurrence: { + frequency: 'daily', + interval: 1, + count: 2 + } + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.tasks).toHaveLength(2); + }); + }); +}); diff --git a/src/tests/unit/handlers/CreateTaskListHandler.test.ts b/src/tests/unit/handlers/CreateTaskListHandler.test.ts new file mode 100644 index 0000000..c7c5ecb --- /dev/null +++ b/src/tests/unit/handlers/CreateTaskListHandler.test.ts @@ -0,0 +1,236 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { CreateTaskListHandler } from '../../../handlers/core/CreateTaskListHandler.js'; +import { OAuth2Client } from 'google-auth-library'; +import { tasks_v1 } from 'googleapis'; + +describe('CreateTaskListHandler', () => { + let handler: CreateTaskListHandler; + let mockAuth: OAuth2Client; + let mockTasks: { + tasklists: { + insert: ReturnType; + }; + }; + + beforeEach(() => { + handler = new CreateTaskListHandler(); + mockAuth = {} as OAuth2Client; + + // Mock the getTasks method to return our mock tasks client + mockTasks = { + tasklists: { + insert: vi.fn() + } + }; + vi.spyOn(handler as any, 'getTasks').mockReturnValue(mockTasks); + }); + + describe('successful task list creation', () => { + it('should create a task list with minimal parameters', async () => { + const mockResponse: tasks_v1.Schema$TaskList = { + id: 'tasklist123', + title: 'My Task List', + updated: '2024-01-15T10:00:00Z' + }; + + mockTasks.tasklists.insert.mockResolvedValue({ data: mockResponse }); + + const accounts = new Map([['default', mockAuth]]); + const result = await handler.runTool( + { + title: 'My Task List' + }, + accounts + ); + + expect(result.content).toHaveLength(1); + expect(result.content[0].type).toBe('text'); + + const response = JSON.parse(result.content[0].text); + expect(response.taskList.id).toBe('tasklist123'); + expect(response.taskList.title).toBe('My Task List'); + expect(response.message).toBe('Task list "My Task List" created successfully'); + expect(response.accountId).toBe('default'); + + expect(mockTasks.tasklists.insert).toHaveBeenCalledWith({ + requestBody: { + title: 'My Task List' + } + }); + }); + + it('should create a task list with a specific account', async () => { + const mockResponse: tasks_v1.Schema$TaskList = { + id: 'tasklist456', + title: 'Work Tasks', + updated: '2024-01-15T10:00:00Z' + }; + + mockTasks.tasklists.insert.mockResolvedValue({ data: mockResponse }); + + const accounts = new Map([ + ['personal', mockAuth], + ['work', mockAuth] + ]); + + const result = await handler.runTool( + { + title: 'Work Tasks', + account: 'work' + }, + accounts + ); + + expect(result.content).toHaveLength(1); + const response = JSON.parse(result.content[0].text); + expect(response.taskList.id).toBe('tasklist456'); + expect(response.accountId).toBe('work'); + }); + + it('should handle task list titles with special characters', async () => { + const mockResponse: tasks_v1.Schema$TaskList = { + id: 'tasklist789', + title: 'Tasks: High Priority! (Q1 2024)', + updated: '2024-01-15T10:00:00Z' + }; + + mockTasks.tasklists.insert.mockResolvedValue({ data: mockResponse }); + + const accounts = new Map([['default', mockAuth]]); + const result = await handler.runTool( + { + title: 'Tasks: High Priority! (Q1 2024)' + }, + accounts + ); + + expect(result.content).toHaveLength(1); + const response = JSON.parse(result.content[0].text); + expect(response.taskList.title).toBe('Tasks: High Priority! (Q1 2024)'); + }); + }); + + describe('error handling', () => { + it('should handle API errors gracefully', async () => { + mockTasks.tasklists.insert.mockRejectedValue( + new Error('API Error: Request failed') + ); + + const accounts = new Map([['default', mockAuth]]); + + // Mock handleGoogleApiError to return a proper error response + vi.spyOn(handler as any, 'handleGoogleApiError').mockReturnValue({ + content: [{ + type: 'text', + text: JSON.stringify({ + error: 'API Error: Request failed', + code: 'GOOGLE_API_ERROR' + }) + }], + isError: true + }); + + const result = await handler.runTool( + { + title: 'My Task List' + }, + accounts + ); + + expect(result.isError).toBe(true); + expect(result.content[0].type).toBe('text'); + const response = JSON.parse(result.content[0].text); + expect(response.error).toBeDefined(); + }); + + it('should handle missing response data', async () => { + mockTasks.tasklists.insert.mockResolvedValue({ data: null }); + + const accounts = new Map([['default', mockAuth]]); + + // Mock handleGoogleApiError to return a proper error response + vi.spyOn(handler as any, 'handleGoogleApiError').mockReturnValue({ + content: [{ + type: 'text', + text: JSON.stringify({ + error: 'Failed to create task list - no data returned', + code: 'INTERNAL_ERROR' + }) + }], + isError: true + }); + + const result = await handler.runTool( + { + title: 'My Task List' + }, + accounts + ); + + // Should be handled by handleGoogleApiError + expect(result.content[0].type).toBe('text'); + expect(result.isError).toBe(true); + }); + + it('should handle account not found', async () => { + const accounts = new Map([['default', mockAuth]]); + + // Mock getClientForAccount to throw an error + vi.spyOn(handler as any, 'getClientForAccount').mockImplementation(() => { + throw new Error('Account not found: work'); + }); + + // Mock handleGoogleApiError to return a proper error response + vi.spyOn(handler as any, 'handleGoogleApiError').mockReturnValue({ + content: [{ + type: 'text', + text: JSON.stringify({ + error: 'Account not found: work', + code: 'ACCOUNT_NOT_FOUND' + }) + }], + isError: true + }); + + const result = await handler.runTool( + { + title: 'My Task List', + account: 'work' + }, + accounts + ); + + expect(result.isError).toBe(true); + }); + }); + + describe('response format', () => { + it('should include all required fields in the response', async () => { + const mockResponse: tasks_v1.Schema$TaskList = { + id: 'tasklist123', + title: 'My Task List', + updated: '2024-01-15T10:00:00Z', + selfLink: 'https://www.googleapis.com/tasks/v1/users/@me/lists/tasklist123' + }; + + mockTasks.tasklists.insert.mockResolvedValue({ data: mockResponse }); + + const accounts = new Map([['default', mockAuth]]); + const result = await handler.runTool( + { + title: 'My Task List' + }, + accounts + ); + + const response = JSON.parse(result.content[0].text); + expect(response).toHaveProperty('taskList'); + expect(response).toHaveProperty('accountId'); + expect(response).toHaveProperty('message'); + expect(response.taskList).toHaveProperty('id'); + expect(response.taskList).toHaveProperty('title'); + expect(response.taskList).toHaveProperty('updated'); + expect(response.taskList).toHaveProperty('selfLink'); + }); + }); +}); diff --git a/src/tests/unit/handlers/DeleteTaskHandler.test.ts b/src/tests/unit/handlers/DeleteTaskHandler.test.ts new file mode 100644 index 0000000..9e9baba --- /dev/null +++ b/src/tests/unit/handlers/DeleteTaskHandler.test.ts @@ -0,0 +1,138 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { OAuth2Client } from 'google-auth-library'; +import { DeleteTaskHandler } from '../../../handlers/core/DeleteTaskHandler.js'; + +// Mock tasks functions +const mockTasksGet = vi.fn(); +const mockTasksDelete = vi.fn(); + +// Mock googleapis +vi.mock('googleapis', () => ({ + google: { + tasks: () => ({ + tasks: { + get: mockTasksGet, + delete: mockTasksDelete + } + }) + } +})); + +describe('DeleteTaskHandler', () => { + let handler: DeleteTaskHandler; + let mockAccounts: Map; + let mockClient: OAuth2Client; + + beforeEach(() => { + vi.clearAllMocks(); + + handler = new DeleteTaskHandler(); + mockClient = { + credentials: { access_token: 'test-token' } + } as unknown as OAuth2Client; + + mockAccounts = new Map([['work', mockClient]]); + }); + + it('should delete task successfully', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task to delete' } + }); + mockTasksDelete.mockResolvedValue({}); + + const result = await handler.runTool({ + account: 'work', + taskId: 'task1' + }, mockAccounts); + + expect(result.content).toHaveLength(1); + const response = JSON.parse(result.content[0].text); + expect(response.success).toBe(true); + expect(response.taskId).toBe('task1'); + expect(response.message).toContain('Task to delete'); + expect(response.message).toContain('deleted successfully'); + }); + + it('should use @default task list when no taskListId specified', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task' } + }); + mockTasksDelete.mockResolvedValue({}); + + await handler.runTool({ account: 'work', taskId: 'task1' }, mockAccounts); + + expect(mockTasksDelete).toHaveBeenCalledWith( + expect.objectContaining({ tasklist: '@default' }) + ); + }); + + it('should use specified taskListId', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task' } + }); + mockTasksDelete.mockResolvedValue({}); + + await handler.runTool({ + account: 'work', + taskId: 'task1', + taskListId: 'custom-list' + }, mockAccounts); + + expect(mockTasksDelete).toHaveBeenCalledWith( + expect.objectContaining({ tasklist: 'custom-list' }) + ); + }); + + it('should proceed with deletion even if get task fails', async () => { + // Simulate task not found but proceed with deletion anyway + mockTasksGet.mockRejectedValue(new Error('Not found')); + mockTasksDelete.mockResolvedValue({}); + + const result = await handler.runTool({ + account: 'work', + taskId: 'task1' + }, mockAccounts); + + // Should still succeed - uses taskId as title fallback + const response = JSON.parse(result.content[0].text); + expect(response.success).toBe(true); + expect(response.taskId).toBe('task1'); + }); + + it('should throw error for invalid account', async () => { + await expect( + handler.runTool({ account: 'nonexistent', taskId: 'task1' }, mockAccounts) + ).rejects.toThrow('Account "nonexistent" not found'); + }); + + it('should include account ID in response', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task' } + }); + mockTasksDelete.mockResolvedValue({}); + + const result = await handler.runTool({ + account: 'work', + taskId: 'task1' + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.accountId).toBe('work'); + }); + + it('should include taskListId in response', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task' } + }); + mockTasksDelete.mockResolvedValue({}); + + const result = await handler.runTool({ + account: 'work', + taskId: 'task1', + taskListId: 'my-list' + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.taskListId).toBe('my-list'); + }); +}); diff --git a/src/tests/unit/handlers/GetTaskHandler.test.ts b/src/tests/unit/handlers/GetTaskHandler.test.ts new file mode 100644 index 0000000..895ab0b --- /dev/null +++ b/src/tests/unit/handlers/GetTaskHandler.test.ts @@ -0,0 +1,137 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { OAuth2Client } from 'google-auth-library'; +import { GetTaskHandler } from '../../../handlers/core/GetTaskHandler.js'; + +// Mock tasks.get function +const mockTasksGet = vi.fn(); + +// Mock googleapis +vi.mock('googleapis', () => ({ + google: { + tasks: () => ({ + tasks: { + get: mockTasksGet + } + }) + } +})); + +describe('GetTaskHandler', () => { + let handler: GetTaskHandler; + let mockAccounts: Map; + let mockClient: OAuth2Client; + + beforeEach(() => { + vi.clearAllMocks(); + + handler = new GetTaskHandler(); + mockClient = { + credentials: { access_token: 'test-token' } + } as unknown as OAuth2Client; + + mockAccounts = new Map([['work', mockClient]]); + }); + + it('should get a task successfully', async () => { + const mockTask = { + data: { + id: 'task1', + title: 'Buy groceries', + status: 'needsAction', + due: '2024-01-20T00:00:00Z', + notes: 'Milk, eggs, bread', + position: '00000000000000000001', + updated: '2024-01-15T10:00:00Z', + selfLink: 'https://www.googleapis.com/tasks/v1/lists/@default/tasks/task1' + } + }; + + mockTasksGet.mockResolvedValue(mockTask); + + const result = await handler.runTool({ + account: 'work', + taskId: 'task1' + }, mockAccounts); + + expect(result.content).toHaveLength(1); + expect(result.content[0].type).toBe('text'); + + const response = JSON.parse(result.content[0].text); + expect(response.task.id).toBe('task1'); + expect(response.task.title).toBe('Buy groceries'); + expect(response.task.status).toBe('needsAction'); + expect(response.task.due).toBe('2024-01-20T00:00:00Z'); + expect(response.task.notes).toBe('Milk, eggs, bread'); + expect(response.accountId).toBe('work'); + }); + + it('should use @default task list when no taskListId specified', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Test Task' } + }); + + await handler.runTool({ account: 'work', taskId: 'task1' }, mockAccounts); + + expect(mockTasksGet).toHaveBeenCalledWith( + expect.objectContaining({ + tasklist: '@default', + task: 'task1' + }) + ); + }); + + it('should use specified taskListId', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Test Task' } + }); + + await handler.runTool({ + account: 'work', + taskId: 'task1', + taskListId: 'custom-list' + }, mockAccounts); + + expect(mockTasksGet).toHaveBeenCalledWith( + expect.objectContaining({ + tasklist: 'custom-list', + task: 'task1' + }) + ); + }); + + it('should throw error when task not found', async () => { + mockTasksGet.mockResolvedValue({ data: null }); + + await expect( + handler.runTool({ account: 'work', taskId: 'nonexistent' }, mockAccounts) + ).rejects.toThrow('Task not found: nonexistent'); + }); + + it('should throw error for invalid account', async () => { + await expect( + handler.runTool({ account: 'nonexistent', taskId: 'task1' }, mockAccounts) + ).rejects.toThrow('Account "nonexistent" not found'); + }); + + it('should handle completed task', async () => { + const mockTask = { + data: { + id: 'task1', + title: 'Completed Task', + status: 'completed', + completed: '2024-01-15T10:00:00Z' + } + }; + + mockTasksGet.mockResolvedValue(mockTask); + + const result = await handler.runTool({ + account: 'work', + taskId: 'task1' + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.task.status).toBe('completed'); + expect(response.task.completed).toBe('2024-01-15T10:00:00Z'); + }); +}); diff --git a/src/tests/unit/handlers/ListTaskListsHandler.test.ts b/src/tests/unit/handlers/ListTaskListsHandler.test.ts new file mode 100644 index 0000000..ff24bdf --- /dev/null +++ b/src/tests/unit/handlers/ListTaskListsHandler.test.ts @@ -0,0 +1,118 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { OAuth2Client } from 'google-auth-library'; +import { ListTaskListsHandler } from '../../../handlers/core/ListTaskListsHandler.js'; + +// Mock tasklists.list function +const mockTasklistsList = vi.fn(); + +// Mock googleapis +vi.mock('googleapis', () => ({ + google: { + tasks: () => ({ + tasklists: { + list: mockTasklistsList + } + }) + } +})); + +describe('ListTaskListsHandler', () => { + let handler: ListTaskListsHandler; + let mockAccounts: Map; + let mockClient: OAuth2Client; + + beforeEach(() => { + vi.clearAllMocks(); + + handler = new ListTaskListsHandler(); + mockClient = { + credentials: { access_token: 'test-token' } + } as unknown as OAuth2Client; + + mockAccounts = new Map([['work', mockClient]]); + }); + + it('should list task lists successfully', async () => { + const mockTaskLists = { + data: { + items: [ + { + id: 'list1', + title: 'My Tasks', + updated: '2024-01-15T10:00:00Z', + selfLink: 'https://www.googleapis.com/tasks/v1/users/@me/lists/list1' + }, + { + id: 'list2', + title: 'Work Tasks', + updated: '2024-01-14T10:00:00Z', + selfLink: 'https://www.googleapis.com/tasks/v1/users/@me/lists/list2' + } + ] + } + }; + + mockTasklistsList.mockResolvedValue(mockTaskLists); + + const result = await handler.runTool({ account: 'work' }, mockAccounts); + + expect(result.content).toHaveLength(1); + expect(result.content[0].type).toBe('text'); + + const response = JSON.parse(result.content[0].text); + expect(response.taskLists).toHaveLength(2); + expect(response.totalCount).toBe(2); + expect(response.accountId).toBe('work'); + expect(response.taskLists[0].id).toBe('list1'); + expect(response.taskLists[0].title).toBe('My Tasks'); + expect(response.taskLists[1].id).toBe('list2'); + expect(response.taskLists[1].title).toBe('Work Tasks'); + }); + + it('should return empty list when no task lists exist', async () => { + mockTasklistsList.mockResolvedValue({ data: { items: [] } }); + + const result = await handler.runTool({ account: 'work' }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.taskLists).toHaveLength(0); + expect(response.totalCount).toBe(0); + }); + + it('should handle null items response', async () => { + mockTasklistsList.mockResolvedValue({ data: {} }); + + const result = await handler.runTool({ account: 'work' }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.taskLists).toHaveLength(0); + expect(response.totalCount).toBe(0); + }); + + it('should use first account when none specified', async () => { + mockTasklistsList.mockResolvedValue({ + data: { + items: [{ id: 'list1', title: 'Tasks' }] + } + }); + + const result = await handler.runTool({}, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.taskLists).toHaveLength(1); + }); + + it('should throw error when no accounts available', async () => { + const emptyAccounts = new Map(); + + await expect(handler.runTool({}, emptyAccounts)).rejects.toThrow( + 'No authenticated accounts available' + ); + }); + + it('should throw error for invalid account', async () => { + await expect( + handler.runTool({ account: 'nonexistent' }, mockAccounts) + ).rejects.toThrow('Account "nonexistent" not found'); + }); +}); diff --git a/src/tests/unit/handlers/ListTasksHandler.test.ts b/src/tests/unit/handlers/ListTasksHandler.test.ts new file mode 100644 index 0000000..a4ef99a --- /dev/null +++ b/src/tests/unit/handlers/ListTasksHandler.test.ts @@ -0,0 +1,152 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { OAuth2Client } from 'google-auth-library'; +import { ListTasksHandler } from '../../../handlers/core/ListTasksHandler.js'; + +// Mock tasks.list function +const mockTasksList = vi.fn(); + +// Mock googleapis +vi.mock('googleapis', () => ({ + google: { + tasks: () => ({ + tasks: { + list: mockTasksList + } + }) + } +})); + +describe('ListTasksHandler', () => { + let handler: ListTasksHandler; + let mockAccounts: Map; + let mockClient: OAuth2Client; + + beforeEach(() => { + vi.clearAllMocks(); + + handler = new ListTasksHandler(); + mockClient = { + credentials: { access_token: 'test-token' } + } as unknown as OAuth2Client; + + mockAccounts = new Map([['work', mockClient]]); + }); + + it('should list tasks successfully', async () => { + const mockTasks = { + data: { + items: [ + { + id: 'task1', + title: 'Buy groceries', + status: 'needsAction', + due: '2024-01-20T00:00:00Z', + notes: 'Milk, eggs, bread' + }, + { + id: 'task2', + title: 'Call mom', + status: 'completed', + completed: '2024-01-15T10:00:00Z' + } + ] + } + }; + + mockTasksList.mockResolvedValue(mockTasks); + + const result = await handler.runTool({ account: 'work' }, mockAccounts); + + expect(result.content).toHaveLength(1); + expect(result.content[0].type).toBe('text'); + + const response = JSON.parse(result.content[0].text); + expect(response.tasks).toHaveLength(2); + expect(response.totalCount).toBe(2); + expect(response.accountId).toBe('work'); + expect(response.tasks[0].id).toBe('task1'); + expect(response.tasks[0].title).toBe('Buy groceries'); + expect(response.tasks[0].status).toBe('needsAction'); + expect(response.tasks[1].id).toBe('task2'); + expect(response.tasks[1].status).toBe('completed'); + }); + + it('should use @default task list when no taskListId specified', async () => { + mockTasksList.mockResolvedValue({ data: { items: [] } }); + + await handler.runTool({ account: 'work' }, mockAccounts); + + expect(mockTasksList).toHaveBeenCalledWith( + expect.objectContaining({ + tasklist: '@default' + }) + ); + }); + + it('should use specified taskListId', async () => { + mockTasksList.mockResolvedValue({ data: { items: [] } }); + + await handler.runTool({ account: 'work', taskListId: 'custom-list' }, mockAccounts); + + expect(mockTasksList).toHaveBeenCalledWith( + expect.objectContaining({ + tasklist: 'custom-list' + }) + ); + }); + + it('should filter by showCompleted', async () => { + mockTasksList.mockResolvedValue({ data: { items: [] } }); + + await handler.runTool({ account: 'work', showCompleted: false }, mockAccounts); + + expect(mockTasksList).toHaveBeenCalledWith( + expect.objectContaining({ + showCompleted: false + }) + ); + }); + + it('should filter by dueMin and dueMax', async () => { + mockTasksList.mockResolvedValue({ data: { items: [] } }); + + await handler.runTool({ + account: 'work', + dueMin: '2024-01-01T00:00:00Z', + dueMax: '2024-01-31T23:59:59Z' + }, mockAccounts); + + expect(mockTasksList).toHaveBeenCalledWith( + expect.objectContaining({ + dueMin: '2024-01-01T00:00:00Z', + dueMax: '2024-01-31T23:59:59Z' + }) + ); + }); + + it('should return empty list when no tasks exist', async () => { + mockTasksList.mockResolvedValue({ data: { items: [] } }); + + const result = await handler.runTool({ account: 'work' }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.tasks).toHaveLength(0); + expect(response.totalCount).toBe(0); + }); + + it('should handle null items response', async () => { + mockTasksList.mockResolvedValue({ data: {} }); + + const result = await handler.runTool({ account: 'work' }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.tasks).toHaveLength(0); + expect(response.totalCount).toBe(0); + }); + + it('should throw error for invalid account', async () => { + await expect( + handler.runTool({ account: 'nonexistent' }, mockAccounts) + ).rejects.toThrow('Account "nonexistent" not found'); + }); +}); diff --git a/src/tests/unit/handlers/UpdateTaskHandler.test.ts b/src/tests/unit/handlers/UpdateTaskHandler.test.ts new file mode 100644 index 0000000..1e5b23f --- /dev/null +++ b/src/tests/unit/handlers/UpdateTaskHandler.test.ts @@ -0,0 +1,174 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { OAuth2Client } from 'google-auth-library'; +import { UpdateTaskHandler } from '../../../handlers/core/UpdateTaskHandler.js'; + +// Mock tasks functions +const mockTasksGet = vi.fn(); +const mockTasksUpdate = vi.fn(); + +// Mock googleapis +vi.mock('googleapis', () => ({ + google: { + tasks: () => ({ + tasks: { + get: mockTasksGet, + update: mockTasksUpdate + } + }) + } +})); + +describe('UpdateTaskHandler', () => { + let handler: UpdateTaskHandler; + let mockAccounts: Map; + let mockClient: OAuth2Client; + + beforeEach(() => { + vi.clearAllMocks(); + + handler = new UpdateTaskHandler(); + mockClient = { + credentials: { access_token: 'test-token' } + } as unknown as OAuth2Client; + + mockAccounts = new Map([['work', mockClient]]); + }); + + it('should update task title successfully', async () => { + const existingTask = { + data: { + id: 'task1', + title: 'Old Title', + status: 'needsAction' + } + }; + + const updatedTask = { + data: { + id: 'task1', + title: 'New Title', + status: 'needsAction' + } + }; + + mockTasksGet.mockResolvedValue(existingTask); + mockTasksUpdate.mockResolvedValue(updatedTask); + + const result = await handler.runTool({ + account: 'work', + taskId: 'task1', + title: 'New Title' + }, mockAccounts); + + expect(result.content).toHaveLength(1); + const response = JSON.parse(result.content[0].text); + expect(response.task.title).toBe('New Title'); + expect(response.accountId).toBe('work'); + }); + + it('should update task notes', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task', status: 'needsAction' } + }); + mockTasksUpdate.mockResolvedValue({ + data: { id: 'task1', title: 'Task', notes: 'Updated notes', status: 'needsAction' } + }); + + const result = await handler.runTool({ + account: 'work', + taskId: 'task1', + notes: 'Updated notes' + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.task.notes).toBe('Updated notes'); + }); + + it('should update task due date', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task', status: 'needsAction' } + }); + mockTasksUpdate.mockResolvedValue({ + data: { id: 'task1', title: 'Task', due: '2024-02-01T00:00:00Z', status: 'needsAction' } + }); + + await handler.runTool({ + account: 'work', + taskId: 'task1', + due: '2024-02-01T00:00:00Z' + }, mockAccounts); + + expect(mockTasksUpdate).toHaveBeenCalledWith( + expect.objectContaining({ + requestBody: expect.objectContaining({ + due: '2024-02-01T00:00:00Z' + }) + }) + ); + }); + + it('should update task status', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task', status: 'needsAction' } + }); + mockTasksUpdate.mockResolvedValue({ + data: { id: 'task1', title: 'Task', status: 'completed', completed: '2024-01-15T10:00:00Z' } + }); + + const result = await handler.runTool({ + account: 'work', + taskId: 'task1', + status: 'completed' + }, mockAccounts); + + const response = JSON.parse(result.content[0].text); + expect(response.task.status).toBe('completed'); + }); + + it('should use @default task list when no taskListId specified', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task', status: 'needsAction' } + }); + mockTasksUpdate.mockResolvedValue({ + data: { id: 'task1', title: 'Updated', status: 'needsAction' } + }); + + await handler.runTool({ + account: 'work', + taskId: 'task1', + title: 'Updated' + }, mockAccounts); + + expect(mockTasksGet).toHaveBeenCalledWith( + expect.objectContaining({ tasklist: '@default' }) + ); + expect(mockTasksUpdate).toHaveBeenCalledWith( + expect.objectContaining({ tasklist: '@default' }) + ); + }); + + it('should throw error when task not found', async () => { + mockTasksGet.mockResolvedValue({ data: null }); + + await expect( + handler.runTool({ account: 'work', taskId: 'nonexistent', title: 'New' }, mockAccounts) + ).rejects.toThrow('Task not found: nonexistent'); + }); + + it('should throw error when update fails', async () => { + mockTasksGet.mockResolvedValue({ + data: { id: 'task1', title: 'Task', status: 'needsAction' } + }); + mockTasksUpdate.mockResolvedValue({ data: null }); + + await expect( + handler.runTool({ account: 'work', taskId: 'task1', title: 'New' }, mockAccounts) + ).rejects.toThrow('Failed to update task - no data returned'); + }); + + it('should throw error for invalid account', async () => { + await expect( + handler.runTool({ account: 'nonexistent', taskId: 'task1', title: 'New' }, mockAccounts) + ).rejects.toThrow('Account "nonexistent" not found'); + }); +}); diff --git a/src/tools/registry.ts b/src/tools/registry.ts index 66394ec..0e584e2 100644 --- a/src/tools/registry.ts +++ b/src/tools/registry.ts @@ -5,7 +5,7 @@ import { BaseToolHandler } from "../handlers/core/BaseToolHandler.js"; import { ALLOWED_EVENT_FIELDS } from "../utils/field-mask-builder.js"; import { ServerConfig } from "../config/TransportConfig.js"; -// Import all handlers +// Import all calendar handlers import { ListCalendarsHandler } from "../handlers/core/ListCalendarsHandler.js"; import { ListEventsHandler } from "../handlers/core/ListEventsHandler.js"; import { SearchEventsHandler } from "../handlers/core/SearchEventsHandler.js"; @@ -18,6 +18,18 @@ import { FreeBusyEventHandler } from "../handlers/core/FreeBusyEventHandler.js"; import { GetCurrentTimeHandler } from "../handlers/core/GetCurrentTimeHandler.js"; import { RespondToEventHandler } from "../handlers/core/RespondToEventHandler.js"; +// Import task handlers (conditionally registered when enableTasks is true) +import { ListTaskListsHandler } from "../handlers/core/ListTaskListsHandler.js"; +import { ListTasksHandler } from "../handlers/core/ListTasksHandler.js"; +import { GetTaskHandler } from "../handlers/core/GetTaskHandler.js"; +import { CreateTaskListHandler } from "../handlers/core/CreateTaskListHandler.js"; +import { CreateTaskHandler } from "../handlers/core/CreateTaskHandler.js"; +import { UpdateTaskHandler } from "../handlers/core/UpdateTaskHandler.js"; +import { DeleteTaskHandler } from "../handlers/core/DeleteTaskHandler.js"; + +// Import task schemas +import { TaskSchemas, TASK_TOOL_NAMES } from "./task-schemas.js"; + // Define shared schema fields for reuse // Note: Event datetime fields (start/end) are NOT shared to avoid $ref generation // Each tool defines its own inline schemas for these fields @@ -678,7 +690,7 @@ export type GetCurrentTimeInput = ToolInputs['get-current-time']; export type RespondToEventInput = ToolInputs['respond-to-event']; interface ToolDefinition { - name: keyof typeof ToolSchemas; + name: keyof typeof ToolSchemas | keyof typeof TaskSchemas; description: string; schema: z.ZodType; handler: new () => BaseToolHandler; @@ -840,6 +852,55 @@ export class ToolRegistry { } ]; + /** + * Task tools - only registered when enableTasks is true. + * These require additional OAuth scope: https://www.googleapis.com/auth/tasks + */ + private static taskTools: ToolDefinition[] = [ + { + name: "list-task-lists", + description: "List all task lists for the authenticated user.", + schema: TaskSchemas['list-task-lists'], + handler: ListTaskListsHandler + }, + { + name: "create-task-list", + description: "Create a new task list.", + schema: TaskSchemas['create-task-list'], + handler: CreateTaskListHandler + }, + { + name: "list-tasks", + description: "List tasks in a task list with optional filtering by status and due date.", + schema: TaskSchemas['list-tasks'], + handler: ListTasksHandler + }, + { + name: "get-task", + description: "Get details of a specific task by ID.", + schema: TaskSchemas['get-task'], + handler: GetTaskHandler + }, + { + name: "create-task", + description: "Create a new task in a task list. Supports recurring tasks. Note: Google Tasks only supports date-level due dates (time is ignored).", + schema: TaskSchemas['create-task'], + handler: CreateTaskHandler + }, + { + name: "update-task", + description: "Update an existing task. Use status: 'completed' to mark done, 'needsAction' to reopen.", + schema: TaskSchemas['update-task'], + handler: UpdateTaskHandler + }, + { + name: "delete-task", + description: "Permanently delete a task.", + schema: TaskSchemas['delete-task'], + handler: DeleteTaskHandler + } + ]; + static getToolsWithSchemas() { return this.tools.map(tool => { const jsonSchema = tool.customInputSchema @@ -885,21 +946,28 @@ export class ToolRegistry { /** * Get all available tool names for validation + * @param enableTasks - Whether to include task tool names */ - static getAvailableToolNames(): string[] { - return this.tools.map(t => t.name); + static getAvailableToolNames(enableTasks = false): string[] { + const names = this.tools.map(t => t.name); + if (enableTasks) { + names.push(...this.taskTools.map(t => t.name)); + } + return names; } /** * Validate that all tool names in a list exist + * @param toolNames - Tool names to validate + * @param enableTasks - Whether task tools are enabled * @throws Error if any tool name is invalid */ - static validateToolNames(toolNames: string[]): void { - const availableTools = new Set([...this.getAvailableToolNames(), 'manage-accounts']); + static validateToolNames(toolNames: string[], enableTasks = false): void { + const availableTools = new Set([...this.getAvailableToolNames(enableTasks), 'manage-accounts']); const invalidTools = toolNames.filter(name => !availableTools.has(name)); if (invalidTools.length > 0) { - const available = [...this.getAvailableToolNames(), 'manage-accounts'].join(', '); + const available = [...this.getAvailableToolNames(enableTasks), 'manage-accounts'].join(', '); throw new Error( `Invalid tool name(s): ${invalidTools.join(', ')}. ` + `Available tools: ${available}` @@ -915,29 +983,53 @@ export class ToolRegistry { ) => Promise<{ content: Array<{ type: "text"; text: string }> }>, config?: ServerConfig ) { + const enableTasks = config?.enableTasks ?? false; + + // Log if Tasks feature is enabled + if (enableTasks) { + process.stderr.write(`Google Tasks integration enabled\n`); + } + // Validate enabledTools if provided if (config?.enabledTools) { if (config.enabledTools.length === 0) { throw new Error('Enabled tools list is empty. Provide at least one tool name.'); } - this.validateToolNames(config.enabledTools); + this.validateToolNames(config.enabledTools, enableTasks); const enabledSet = new Set(config.enabledTools); process.stderr.write(`Tool filtering enabled: ${config.enabledTools.join(', ')}\n`); - // Filter and register only enabled tools + // Filter and register only enabled calendar tools for (const tool of this.tools) { if (!enabledSet.has(tool.name)) { continue; } this.registerSingleTool(server, tool, executeWithHandler); } + + // Filter and register only enabled task tools (if enableTasks is true) + if (enableTasks) { + for (const tool of this.taskTools) { + if (!enabledSet.has(tool.name)) { + continue; + } + this.registerSingleTool(server, tool, executeWithHandler); + } + } return; } - // No filtering - register all tools + // No filtering - register all calendar tools for (const tool of this.tools) { this.registerSingleTool(server, tool, executeWithHandler); } + + // Register all task tools if enableTasks is true + if (enableTasks) { + for (const tool of this.taskTools) { + this.registerSingleTool(server, tool, executeWithHandler); + } + } } private static registerSingleTool( diff --git a/src/tools/task-schemas.ts b/src/tools/task-schemas.ts new file mode 100644 index 0000000..57ec99e --- /dev/null +++ b/src/tools/task-schemas.ts @@ -0,0 +1,196 @@ +/** + * Zod schemas for Google Tasks API tools. + * + * This file defines input validation schemas for all task-related tools, + * keeping the main registry.ts focused on calendar tools. + */ + +import { z } from "zod"; + +// Single account schema - consistent with calendar tools +const singleAccountSchema = z.string() + .regex(/^[a-z0-9_-]{1,64}$/, "Account nickname must be 1-64 characters: lowercase letters, numbers, dashes, underscores only") + .optional() + .describe( + "Account nickname (e.g., 'work'). Optional if only one account connected." + ); + +/** + * Task tool schemas for input validation. + */ +export const TaskSchemas = { + /** + * Schema for list-task-lists tool + * Lists all task lists for the authenticated user. + */ + 'list-task-lists': z.object({ + account: singleAccountSchema + }), + + /** + * Schema for list-tasks tool + * Lists tasks within a specific task list with optional filtering. + */ + 'list-tasks': z.object({ + account: singleAccountSchema, + taskListId: z.string() + .default('@default') + .describe("Task list ID (use '@default' for the default task list)"), + showCompleted: z.boolean() + .optional() + .default(true) + .describe("Include completed tasks (default: true)"), + showHidden: z.boolean() + .optional() + .default(false) + .describe("Include hidden tasks (default: false)"), + dueMin: z.string() + .optional() + .describe("Filter by minimum due date (RFC 3339 format, e.g., '2024-01-01T00:00:00Z')"), + dueMax: z.string() + .optional() + .describe("Filter by maximum due date (RFC 3339 format, e.g., '2024-12-31T23:59:59Z')"), + maxResults: z.number() + .int() + .min(1) + .max(100) + .optional() + .default(100) + .describe("Maximum number of tasks to return (1-100, default: 100)") + }), + + /** + * Schema for get-task tool + * Gets details of a specific task by ID. + */ + 'get-task': z.object({ + account: singleAccountSchema, + taskListId: z.string() + .describe("Task list ID (use '@default' for the default task list)"), + taskId: z.string() + .describe("ID of the task to retrieve") + }), + + /** + * Schema for create-task-list tool + * Creates a new task list. + */ + 'create-task-list': z.object({ + account: singleAccountSchema, + title: z.string() + .min(1) + .max(1024) + .describe("Title of the task list (required, max 1024 characters)") + }), + + /** + * Schema for create-task tool + * Creates a new task in the specified task list. + * Note: Google Tasks API only supports date-level due dates, not specific times. + * The time component in the 'due' field will be stored but not used for scheduling. + */ + 'create-task': z.object({ + account: singleAccountSchema, + taskListId: z.string() + .default('@default') + .describe("Task list ID (use '@default' for the default task list)"), + title: z.string() + .min(1) + .max(1024) + .describe("Title of the task (required, max 1024 characters)"), + notes: z.string() + .max(8192) + .optional() + .describe("Description/notes for the task (max 8192 characters)"), + due: z.string() + .optional() + .describe("Due date in RFC 3339 format (e.g., '2024-01-15T00:00:00Z' or date-only '2024-01-15'). Note: Google Tasks only uses the date portion; time is ignored."), + parent: z.string() + .optional() + .describe("Parent task ID to create this as a subtask"), + previous: z.string() + .optional() + .describe("Previous sibling task ID for ordering"), + recurrence: z.object({ + frequency: z.enum(['daily', 'weekly', 'monthly', 'yearly']) + .describe("How often the task repeats"), + interval: z.number() + .int() + .min(1) + .max(999) + .default(1) + .describe("Repeat every N days/weeks/months/years (default: 1)"), + count: z.number() + .int() + .min(1) + .max(365) + .optional() + .describe("Number of occurrences to create (max 365)"), + until: z.string() + .optional() + .describe("End date in YYYY-MM-DD format") + }).optional() + .describe("Recurrence pattern for creating multiple tasks. Note: Google Tasks API doesn't natively support recurring tasks; this creates separate tasks for each occurrence.") + }), + + /** + * Schema for update-task tool + * Updates an existing task with new values. + */ + 'update-task': z.object({ + account: singleAccountSchema, + taskListId: z.string() + .describe("Task list ID (use '@default' for the default task list)"), + taskId: z.string() + .describe("ID of the task to update"), + title: z.string() + .min(1) + .max(1024) + .optional() + .describe("Updated title (max 1024 characters)"), + notes: z.string() + .max(8192) + .optional() + .describe("Updated description/notes (max 8192 characters)"), + due: z.string() + .optional() + .describe("Updated due date in RFC 3339 format"), + status: z.enum(['needsAction', 'completed']) + .optional() + .describe("Updated status: 'needsAction' (incomplete) or 'completed'") + }), + + /** + * Schema for delete-task tool + * Permanently deletes a task. + */ + 'delete-task': z.object({ + account: singleAccountSchema, + taskListId: z.string() + .describe("Task list ID (use '@default' for the default task list)"), + taskId: z.string() + .describe("ID of the task to delete") + }) +} as const; + +// Generate TypeScript types from schemas +export type TaskInputs = { + [K in keyof typeof TaskSchemas]: z.infer +}; + +// Export individual types for convenience +export type ListTaskListsInput = TaskInputs['list-task-lists']; +export type ListTasksInput = TaskInputs['list-tasks']; +export type GetTaskInput = TaskInputs['get-task']; +export type CreateTaskListInput = TaskInputs['create-task-list']; +export type CreateTaskInput = TaskInputs['create-task']; +export type UpdateTaskInput = TaskInputs['update-task']; +export type DeleteTaskInput = TaskInputs['delete-task']; + +// Note: complete-task was intentionally removed as it's just syntactic sugar +// for update-task with status: 'completed'. Use update-task instead. + +/** + * List of all task tool names for validation. + */ +export const TASK_TOOL_NAMES = Object.keys(TaskSchemas) as (keyof typeof TaskSchemas)[]; diff --git a/src/types/task-responses.ts b/src/types/task-responses.ts new file mode 100644 index 0000000..c93f6b6 --- /dev/null +++ b/src/types/task-responses.ts @@ -0,0 +1,101 @@ +/** + * Structured response types for Google Tasks API tools. + * + * These types define the shape of responses returned by task handlers, + * ensuring consistent and well-typed API responses. + */ + +import { FormattedTask, FormattedTaskList } from '../handlers/core/BaseTaskHandler.js'; + +/** + * Response for list-task-lists tool + */ +export interface ListTaskListsResponse { + taskLists: FormattedTaskList[]; + totalCount: number; + accountId: string; +} + +/** + * Response for list-tasks tool + */ +export interface ListTasksResponse { + tasks: FormattedTask[]; + totalCount: number; + taskListId: string; + taskListTitle?: string; + accountId: string; + filters?: { + showCompleted: boolean; + showHidden: boolean; + dueMin?: string; + dueMax?: string; + }; +} + +/** + * Response for get-task tool + */ +export interface GetTaskResponse { + task: FormattedTask; + taskListId: string; + accountId: string; +} + +/** + * Response for create-task-list tool + */ +export interface CreateTaskListResponse { + taskList: FormattedTaskList; + accountId: string; + message: string; +} + +/** + * Response for create-task tool + */ +export interface CreateTaskResponse { + task?: FormattedTask; + tasks?: FormattedTask[]; + taskListId: string; + accountId: string; + message: string; + recurringInfo?: { + frequency: string; + interval: number; + occurrencesCreated: number; + }; +} + +/** + * Response for update-task tool + */ +export interface UpdateTaskResponse { + task: FormattedTask; + taskListId: string; + accountId: string; + message: string; + updatedFields: string[]; +} + +/** + * Response for delete-task tool + */ +export interface DeleteTaskResponse { + success: boolean; + taskId: string; + taskListId: string; + accountId: string; + message: string; +} + +/** + * Common error response structure for task operations + */ +export interface TaskErrorResponse { + error: string; + code: string; + taskId?: string; + taskListId?: string; + accountId?: string; +}