diff --git a/.env.example b/.env.example index d841f3f..40f597e 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,5 @@ # WordPress API configuration -# The standard WordPress REST API endpoint WORDPRESS_API_URL=https://your-wordpress-site.com -# For authentication using Application Passwords (WordPress 5.6+) WORDPRESS_USERNAME=your_username WORDPRESS_PASSWORD=your_app_password +WORDPRESS_SQL_ENDPOINT=/mcp/v1/query diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..56de78c --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,29 @@ +name: Build and Test + +on: + pull_request: + types: [opened, synchronize] + paths-ignore: + - '**.md' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install Dependencies + run: npm install + + - name: Build Project + run: npm run build + + - name: Run Tests + run: npm test diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..5f4e19f --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,41 @@ +name: Publish to npm + +on: + push: + branches: + - main + paths-ignore: + - '**.md' + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org/' + scope: '@missionsquad' + + - name: Install Dependencies + run: npm install + + - name: Build Project + run: npm run build + + - name: Run Tests + run: npm test + + - name: Publish to npm + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 7085b5d..0274e12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .env node_modules build -package-lock.json logs *.code-workspace .claude/ diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 43e0a15..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,252 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -This is a WordPress MCP (Model Context Protocol) server that allows interaction with WordPress sites through natural language via MCP-compatible clients like Claude Desktop. The server exposes WordPress REST API functionality as MCP tools. - -## Development Commands - -### Build and Run -```bash -# Install dependencies -npm install - -# Build TypeScript to JavaScript -npm run build - -# Run in development mode with hot reload -npm run dev - -# Run the built server -npm start - -# Clean build artifacts -npm run clean -``` - -### Environment Setup - -#### Single Site Configuration -Create a `.env` file in the project root with: -```env -WORDPRESS_API_URL=https://your-wordpress-site.com -WORDPRESS_USERNAME=wp_username -WORDPRESS_PASSWORD=wp_app_password -``` - -#### Multi-Site Configuration -For managing multiple WordPress sites: -```env -# Site 1 (Production) -WORDPRESS_1_URL=https://production-site.com -WORDPRESS_1_USERNAME=admin -WORDPRESS_1_PASSWORD=app_password_1 -WORDPRESS_1_ID=production -WORDPRESS_1_DEFAULT=true -WORDPRESS_1_ALIASES=prod,main - -# Site 2 (Staging) -WORDPRESS_2_URL=https://staging-site.com -WORDPRESS_2_USERNAME=admin -WORDPRESS_2_PASSWORD=app_password_2 -WORDPRESS_2_ID=staging -WORDPRESS_2_ALIASES=stage,dev -``` - -The app password can be generated from WordPress admin panel following the [Application Passwords guide](https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide#Getting-Credentials). - -## Architecture - -### Core Components - -1. **MCP Server (`src/server.ts`)**: - - Entry point that initializes the MCP server using the ModelContextProtocol SDK - - Registers all WordPress tools with their handlers - - Uses StdioServerTransport for communication with Claude Desktop - - Validates environment variables and establishes WordPress connection on startup - -2. **Site Manager (`src/config/site-manager.ts`)**: - - Manages multiple WordPress site configurations - - Lazy loads site configurations from environment variables - - Maintains separate authenticated Axios clients for each site - - Provides site detection from context (domain mentions, aliases, site IDs) - - Supports both numbered multi-site config and legacy single-site config - -3. **WordPress Client (`src/wordpress.ts`)**: - - Manages authenticated Axios instance for WordPress REST API calls - - Integrates with SiteManager for multi-site support - - Handles authentication using Basic Auth with application passwords - - Provides `makeWordPressRequest()` wrapper for all API calls with optional `siteId` parameter - - Includes logging to `logs/wordpress-api.log` for debugging - - Special handler `searchWordPressPluginRepository()` for WordPress.org plugin search - -4. **Tool System (`src/tools/`)**: - - Each WordPress entity (posts, pages, media, etc.) has its own module - - Each module exports tools array and handlers object - - Tools use Zod schemas for input validation and type safety - - All tools support optional `site_id` parameter for multi-site support - - All tools are aggregated in `src/tools/index.ts` - -### Tool Pattern - -Each tool module follows this pattern: -```typescript -// Define Zod schemas for input validation -const listSchema = z.object({...}); -const getSchema = z.object({...}); -const createSchema = z.object({...}); -const updateSchema = z.object({...}); -const deleteSchema = z.object({...}); - -// Export tools array with MCP tool definitions -export const entityTools: Tool[] = [ - { name: "list_entity", description: "...", inputSchema: {...} }, - { name: "get_entity", description: "...", inputSchema: {...} }, - { name: "create_entity", description: "...", inputSchema: {...} }, - { name: "update_entity", description: "...", inputSchema: {...} }, - { name: "delete_entity", description: "...", inputSchema: {...} } -]; - -// Export handlers object with async functions -export const entityHandlers = { - list_entity: async (params) => {...}, - get_entity: async (params) => {...}, - create_entity: async (params) => {...}, - update_entity: async (params) => {...}, - delete_entity: async (params) => {...} -}; -``` - -### Unified Tool Architecture - -The MCP server uses a **unified tool approach** to reduce complexity and tool count from ~65 to ~35 tools. Instead of separate tools for posts, pages, and custom post types, there are now unified tools that handle all content types. - -#### **Unified Content Tools** (`unified-content.ts`) - 8 tools -Handles ALL content types (posts, pages, custom post types) with a single set of tools: -- `list_content` - List any content type with filtering and pagination -- `get_content` - Get specific content by ID and type -- `create_content` - Create new content of any type -- `update_content` - Update existing content of any type -- `delete_content` - Delete content of any type -- `discover_content_types` - Find all available content types -- `find_content_by_url` - Smart URL resolver with optional update -- `get_content_by_slug` - Search by slug across content types - -#### **Unified Taxonomy Tools** (`unified-taxonomies.ts`) - 8 tools -Handles ALL taxonomies (categories, tags, custom taxonomies) with a single set of tools: -- `discover_taxonomies` - Find all available taxonomies -- `list_terms` - List terms in any taxonomy -- `get_term` - Get specific term by ID -- `create_term` - Create new term in any taxonomy -- `update_term` - Update existing term -- `delete_term` - Delete term from any taxonomy -- `assign_terms_to_content` - Assign terms to any content type -- `get_content_terms` - Get all terms for any content - -#### **Site Management Tools** (`site-management.ts`) - 3 tools -- `list_sites` - List all configured WordPress sites -- `get_site` - Get details about a specific site -- `test_site` - Test connection to a WordPress site - -#### **Other Specialized Tools** -- **Media** (`media.ts`): Media library management (~5 tools) -- **Users** (`users.ts`): User management (~5 tools) -- **Comments** (`comments.ts`): Comment management (~5 tools) -- **Plugins** (`plugins.ts`): Plugin activation/deactivation (~5 tools) -- **Plugin Repository** (`plugin-repository.ts`): WordPress.org plugin search (~2 tools) -- **SQL Queries** (`sql-query.ts`): Execute read-only database queries (1 tool, requires custom endpoint) - - **Note**: Uses `/mcp/v1/query` endpoint by default; customize via `WORDPRESS_SQL_ENDPOINT` environment variable - -### Key Features - -#### Smart URL Resolution -The `find_content_by_url` tool can: -- Take any WordPress URL and automatically find the corresponding content -- Detect the content type from URL patterns (e.g., `/documentation/` → documentation CPT) -- Optionally update the content in a single operation -- Cache content type information to minimize API calls - -Example: Given `https://site.com/documentation/api-guide/`, it will: -1. Extract the slug `api-guide` -2. Detect hints suggesting a documentation content type -3. Search efficiently across relevant content types -4. Return or update the found content - -#### Unified Content Management -All content operations use a single `content_type` parameter: -```json -{ - "content_type": "post", // for blog posts - "content_type": "page", // for static pages - "content_type": "product", // for custom post types - "content_type": "documentation" // for custom post types -} -``` - -#### Unified Taxonomy Management -All taxonomy operations use a single `taxonomy` parameter: -```json -{ - "taxonomy": "category", // for categories - "taxonomy": "post_tag", // for tags - "taxonomy": "product_category", // for custom taxonomies - "taxonomy": "skill" // for custom taxonomies -} -``` - -#### Multi-Site Support -All tools accept an optional `site_id` parameter to target specific sites: -```json -{ - "content_type": "post", - "site_id": "production" // Optional - targets specific site -} -``` - -If `site_id` is not provided, the default site is used. Sites can be managed via: -- `list_sites` - See all configured sites -- `get_site` - Get details about a site -- `test_site` - Test connection to a site - -## TypeScript Configuration - -- Target: ES2022 with ESNext modules -- Strict mode enabled -- Source in `src/`, builds to `build/` -- Declaration files generated - -## Claude Desktop Integration - -The server integrates with Claude Desktop via the configuration in `claude_desktop_config.json`: -```json -{ - "mcpServers": { - "wordpress": { - "command": "npx", - "args": ["-y", "@instawp/mcp-wp"], - "env": { - "WORDPRESS_API_URL": "https://your-site.com", - "WORDPRESS_USERNAME": "username", - "WORDPRESS_PASSWORD": "app_password" - } - } - } -} -``` - -## Error Handling - -- All API requests are wrapped in try-catch blocks -- Errors are logged to `logs/wordpress-api.log` with full request/response details -- Process signals (SIGTERM, SIGINT) are handled gracefully -- Uncaught exceptions and rejections trigger proper shutdown - -## Key Dependencies - -- `@modelcontextprotocol/sdk`: MCP protocol implementation -- `axios`: HTTP client for WordPress REST API -- `zod`: Runtime type validation for tool inputs -- `dotenv`: Environment variable management -- `tsx`: TypeScript execution for development \ No newline at end of file diff --git a/MISSION_SQUAD_HIDDEN_SECRET_REFACTOR_GUIDE.md b/MISSION_SQUAD_HIDDEN_SECRET_REFACTOR_GUIDE.md new file mode 100644 index 0000000..f2f3c3d --- /dev/null +++ b/MISSION_SQUAD_HIDDEN_SECRET_REFACTOR_GUIDE.md @@ -0,0 +1,661 @@ +# mcp-wordpress MissionSquad Hidden Secret Refactor Guide + +**Version:** 1.0 +**Date:** 2026-04-08 +**Status:** Implementation-Ready + +## 1. Purpose + +This guide defines the recommended refactor for making `mcp-wordpress` compatible with MissionSquad hidden secret injection. + +It is package-specific and implementation-oriented. It is based on a verified review of the current `mcp-wordpress` codebase and is intended to be sufficient on its own for an engineer to perform the refactor. + +## 2. Executive Summary + +`mcp-wordpress` is not currently MissionSquad hidden-secret compatible. + +The main reason is architectural: + +- it uses the low-level MCP SDK server helper instead of `@missionsquad/fastmcp` +- its handlers only receive public tool args +- it resolves WordPress credentials from process environment only +- it initializes and tests the WordPress connection at server startup, before any per-user hidden values could exist +- it keeps authenticated clients in a global singleton keyed to env-defined sites + +MissionSquad compatibility requires a different model: + +1. The server must declare hidden keys in MissionSquad `secretNames`. +2. MissionSquad must inject those values per user per tool call. +3. The server must read those values from FastMCP `context.extraArgs`. +4. Client creation must become request-scoped and lazy. + +## 3. Verified Current State + +## 3.1 Package Runtime + +The package currently: + +- is TypeScript +- builds to `build/` +- runs a stdio MCP server +- uses `@modelcontextprotocol/sdk` directly +- targets Node `>=18` + +## 3.2 Authentication Model + +Current WordPress authentication is environment-driven only. + +Single-site env mode: + +- `WORDPRESS_API_URL` +- `WORDPRESS_USERNAME` +- `WORDPRESS_PASSWORD` + +Multi-site env mode: + +- `WORDPRESS_1_URL` +- `WORDPRESS_1_USERNAME` +- `WORDPRESS_1_PASSWORD` +- `WORDPRESS_1_ID` +- `WORDPRESS_1_DEFAULT` +- `WORDPRESS_1_ALIASES` +- repeated up to `WORDPRESS_10_*` + +The authenticated Axios client is created with a Basic Auth header using: + +```text +base64(username:password) +``` + +## 3.3 Server Runtime Pattern + +`src/server.ts`: + +- loads `.env` +- creates the MCP server +- registers tools +- calls `initWordPress()` during startup +- fails startup if a WordPress connection cannot be established immediately + +This is incompatible with MissionSquad hidden injection because MissionSquad stdio servers start once and serve many users. Per-user secrets are only available at tool-call time, not process start. + +## 3.4 Global Site Manager Pattern + +`src/config/site-manager.ts` currently: + +- lazily loads sites from environment variables +- stores site configs in a global singleton +- caches Axios clients by site id +- chooses a default site + +This is incompatible with MissionSquad’s per-user hidden secret model because: + +- config is process-global, not per user +- authenticated clients are cached globally +- the server has no request-scoped config source + +## 3.5 Tool Surface Findings + +### Good news + +- public tool schemas do not currently expose the server’s own WordPress auth fields +- there is no current visible-schema collision with the future hidden keys if new names are chosen carefully + +### Important constraints + +- many public schemas already use generic names like `url`, `username`, and `password` +- therefore the hidden MissionSquad keys must **not** use those generic names + +Examples of collisions to avoid: + +- `username` would collide with `create_user` +- `password` would collide with `create_user` and `update_user` +- `url` would collide with `find_content_by_url` and user fields + +### Site-management leakage + +`src/tools/site-management.ts` currently returns: + +- site URLs +- WordPress usernames +- aliases + +That is not an acceptable default pattern for a MissionSquad hidden-secret server, because the package would be exposing hidden runtime configuration back to the LLM. + +### Multi-site inconsistency + +The README claims broad multi-site support, but the implementation is partial: + +- `site_id` appears in `site-management.ts` and `unified-content.ts` +- most other tool modules do not accept `site_id` + +This means the current multi-site story is incomplete even before any MissionSquad refactor. + +### Multi-site cache bug + +`src/tools/unified-content.ts` caches discovered content types globally, not per site. + +That means content type discovery for one site can be reused incorrectly for another site. + +## 3.6 Other Verified Hygiene Issues + +These are not the main hidden-secret blockers, but they should be cleaned up during or immediately after the refactor: + +- `src/cli.ts` checks `WORDPRESS_APP_PASSWORD`, while the rest of the package uses `WORDPRESS_PASSWORD` +- `src/tools/media.ts` dynamically imports `form-data`, but `form-data` is not declared in `package.json` +- `fs-extra` and several `zodToJsonSchema` imports appear unused + +## 4. Recommended Product Decision + +## 4.1 Phase 1 Scope: Single Site Per User + +For MissionSquad compatibility, the recommended refactor scope is: + +- one WordPress site per MissionSquad user per installed server +- hidden secrets provide the site URL and credentials for that one site +- local env fallback remains supported for standalone development + +This is the recommended v1 because it is: + +- compatible with MissionSquad’s flat string secret model +- compatible with per-user server installs +- much simpler than preserving env-indexed multi-site secrets +- safer than exposing site inventories to the model + +## 4.2 Explicit Non-Goal For Phase 1 + +Do not preserve the current env-driven multi-site feature as part of the first MissionSquad hidden-secret refactor. + +Reasons: + +- the current multi-site support is incomplete across the tool surface +- site-management tools expose runtime config that should remain hidden +- the current cache strategy is not site-safe +- MissionSquad hidden-secret storage is a flat name/value model, not a first-class multi-site config store + +## 4.3 Phase 2 Option + +If product later requires multi-site hidden support, implement it as a separate phase after single-site hidden mode is stable. + +A future multi-site phase should use a deliberately designed hidden config contract, not the current numbered env-variable model. + +## 5. Target Hidden Secret Contract + +Use the following hidden key names for MissionSquad registration: + +- `wordpressUrl` +- `wordpressUsername` +- `wordpressPassword` +- `wordpressSqlEndpoint` (optional, only if the SQL tool is surfaced) + +These names are recommended because they do not collide with current public tool schema fields. + +Do **not** use: + +- `url` +- `username` +- `password` + +Those names already exist as public business inputs in the current tool surface. + +## 5.1 MissionSquad `secretNames` + +Recommended MissionSquad stdio server definition: + +```json +{ + "name": "mcp-wordpress", + "transportType": "stdio", + "command": "node", + "args": ["/absolute/path/to/build/server.js"], + "secretNames": [ + "wordpressUrl", + "wordpressUsername", + "wordpressPassword", + "wordpressSqlEndpoint" + ], + "enabled": true +} +``` + +If the SQL tool remains unregistered, omit `wordpressSqlEndpoint`. + +## 5.2 Env Fallback Mapping + +For standalone local use, keep these env fallbacks: + +- `WORDPRESS_API_URL` -> `wordpressUrl` +- `WORDPRESS_USERNAME` -> `wordpressUsername` +- `WORDPRESS_PASSWORD` -> `wordpressPassword` +- `WORDPRESS_SQL_ENDPOINT` -> `wordpressSqlEndpoint` + +Do not keep numbered multi-site env variables in the MissionSquad refactor scope. + +## 6. Required Architecture Changes + +## 6.1 Replace Raw SDK Server Wiring With FastMCP + +Current package pattern: + +- raw `McpServer` +- manual tool registration +- handlers that receive only `args` + +Target pattern: + +- `@missionsquad/fastmcp` +- `server.addTool(...)` +- handlers that receive `(args, context)` +- hidden config resolved from `context.extraArgs` + +This is the core compatibility change. + +## 6.2 Make Client Creation Request-Scoped + +Remove startup-time global auth initialization. + +Target behavior: + +- the MCP server starts without requiring any WordPress credentials +- each tool call resolves hidden/env config at execution time +- each tool call creates or retrieves a client for the current resolved config + +Do not keep a global singleton client keyed only to process environment. + +## 6.3 Replace SiteManager With A Config Resolver + +The current `SiteManager` is the wrong abstraction for MissionSquad hidden injection. + +Replace it with a request-scoped resolver that: + +- reads hidden values from `context.extraArgs` +- falls back to single-site env vars for local mode +- validates the resolved config +- returns a typed config object + +Recommended interface: + +```ts +export interface WordPressRequestConfig { + wordpressUrl: string + wordpressUsername: string + wordpressPassword: string + wordpressSqlEndpoint?: string +} + +export function resolveWordPressConfig( + extraArgs: Record | undefined, + defaults: AppConfig, +): WordPressRequestConfig +``` + +## 6.4 Keep Transport/Auth Separate From Tool Inputs + +Tool handlers should not know how WordPress credentials are resolved. + +Each tool should do this: + +```ts +execute: async (args, context) => { + const client = createWordPressClient(context.extraArgs) + return await runTool(client, args) +} +``` + +Not this: + +```ts +execute: async (args) => { + const username = process.env.WORDPRESS_USERNAME + const password = process.env.WORDPRESS_PASSWORD +} +``` + +## 7. File-By-File Refactor Plan + +## 7.1 `package.json` + +Required changes: + +- add `@missionsquad/fastmcp` +- add a test runner such as `vitest` +- add `form-data` if `create_media` URL-upload support remains +- raise the Node engine to `>=20.0.0` +- raise TypeScript to the current MissionSquad standard + +Recommended cleanup: + +- remove unused `fs-extra` +- remove unused `zod-to-json-schema` if it is no longer needed after the server rewrite + +## 7.2 `src/server.ts` + +Replace the current manual MCP SDK server bootstrap with a FastMCP server. + +Required outcomes: + +- no startup `initWordPress()` call +- no startup credential validation +- each tool registered through FastMCP +- handlers receive `context.extraArgs` +- stdout remains reserved for MCP transport + +## 7.3 `src/config/site-manager.ts` + +Do not carry this file forward unchanged. + +Recommended action: + +- remove it from the MissionSquad path entirely + +If local-only multi-site support must be preserved for non-MissionSquad use, move it behind a separate legacy compatibility module and do not make it part of the MissionSquad hidden-secret path. + +## 7.4 `src/wordpress.ts` + +Refactor this file into a stateless request/client layer. + +Required changes: + +- remove global `wpClient` +- remove `initWordPress()` +- stop depending on a global `siteManager` +- add `createWordPressClient(extraArgs)` or `createWordPressClient(config)` +- add `makeWordPressRequest(client, ...)` or a small class that wraps the client + +Recommended split: + +- `src/config.ts` for hidden/env resolution +- `src/wordpress-client.ts` for Axios client creation and request helpers +- keep `src/wordpress.ts` only if you want a thin compatibility re-export + +## 7.5 `src/tools/site-management.ts` + +Recommended Phase 1 action: + +- remove this module from the MissionSquad tool surface + +Reasons: + +- it exposes URLs and usernames +- it is built around env-defined site inventories +- it does not fit the one-site-per-user MissionSquad model + +If you must keep a diagnostic tool, replace the current surface with one sanitized tool: + +- `test_current_site` + +Allowed output: + +- hostname +- REST base +- connection success/failure + +Do not return: + +- username +- password +- hidden key names or values +- full site inventory + +## 7.6 `src/tools/unified-content.ts` + +Required changes: + +- remove `site_id` from the public schema in the MissionSquad refactor path +- remove global content-type cache or key it by resolved site identity +- keep all business fields as public tool inputs + +If single-site MissionSquad mode is chosen, `site_id` is misleading and should not remain public. + +## 7.7 `src/tools/unified-taxonomies.ts`, `plugins.ts`, `media.ts`, `users.ts`, `comments.ts`, `plugin-repository.ts` + +Required changes: + +- migrate them to the new request-scoped client factory +- keep public business schemas intact where possible +- do not add any auth-bearing public fields + +Watch for collisions: + +- `users.ts` already uses public `username` and `password` +- `find_content_by_url` and user schemas already use public `url` + +That is why hidden key names must stay namespaced as `wordpressUrl`, `wordpressUsername`, and `wordpressPassword`. + +## 7.8 `src/tools/sql-query.ts` + +Current state: + +- exists +- uses `WORDPRESS_SQL_ENDPOINT` +- is not currently included in `src/tools/index.ts` + +Recommended decision: + +- leave it out of Phase 1 unless you explicitly want to expose SQL querying in MissionSquad + +If you do include it later: + +- move its endpoint config to hidden/env resolution as `wordpressSqlEndpoint` +- keep it optional + +## 7.9 `src/cli.ts` + +Either: + +- remove this file if it is not needed + +or: + +- fix the env name mismatch so it uses `WORDPRESS_PASSWORD` + +Do not leave the package in a state where CLI docs and runtime expect different password variable names. + +## 8. Recommended Target Module Layout + +```text +src/ + config.ts + errors.ts + index.ts + server.ts + stdio-safe-console.ts + wordpress-client.ts + tools/ + index.ts + unified-content.ts + unified-taxonomies.ts + plugins.ts + media.ts + users.ts + comments.ts + plugin-repository.ts +``` + +Optional: + +- `sql-query.ts` + +Legacy local-only multi-site support, if kept at all, should live in a clearly isolated compatibility module and should not be the default MissionSquad path. + +## 9. Example Hidden Config Resolver + +```ts +import { UserError } from '@missionsquad/fastmcp' + +export interface AppConfig { + defaultWordpressUrl?: string + defaultWordpressUsername?: string + defaultWordpressPassword?: string + defaultWordpressSqlEndpoint?: string +} + +export interface ResolvedWordPressConfig { + wordpressUrl: string + wordpressUsername: string + wordpressPassword: string + wordpressSqlEndpoint?: string +} + +function readHiddenString( + extraArgs: Record | undefined, + key: string, +): string | undefined { + const value = extraArgs?.[key] + if (value === undefined) { + return undefined + } + if (typeof value !== 'string') { + throw new UserError(`Hidden argument "${key}" must be a string when provided.`) + } + const trimmed = value.trim() + if (!trimmed) { + throw new UserError(`Hidden argument "${key}" must be a non-empty string when provided.`) + } + return trimmed +} + +export function resolveWordPressConfig( + extraArgs: Record | undefined, + defaults: AppConfig, +): ResolvedWordPressConfig { + const wordpressUrl = + readHiddenString(extraArgs, 'wordpressUrl') ?? defaults.defaultWordpressUrl + const wordpressUsername = + readHiddenString(extraArgs, 'wordpressUsername') ?? defaults.defaultWordpressUsername + const wordpressPassword = + readHiddenString(extraArgs, 'wordpressPassword') ?? defaults.defaultWordpressPassword + const wordpressSqlEndpoint = + readHiddenString(extraArgs, 'wordpressSqlEndpoint') ?? defaults.defaultWordpressSqlEndpoint + + if (!wordpressUrl || !wordpressUsername || !wordpressPassword) { + throw new UserError( + 'WordPress credentials are required. Provide hidden arguments wordpressUrl, wordpressUsername, and wordpressPassword, or configure the local fallback environment.', + ) + } + + return { + wordpressUrl, + wordpressUsername, + wordpressPassword, + wordpressSqlEndpoint, + } +} +``` + +## 10. Example Request-Scoped WordPress Client + +```ts +import axios, { type AxiosInstance } from 'axios' +import { appConfig, resolveWordPressConfig } from './config.js' + +function normalizeWordPressBaseUrl(wordpressUrl: string): string { + const trimmed = wordpressUrl.endsWith('/') ? wordpressUrl : `${wordpressUrl}/` + if (trimmed.includes('/wp-json/wp/v2/')) { + return trimmed + } + if (trimmed.includes('/wp-json/wp/v2')) { + return `${trimmed}/` + } + return `${trimmed}wp-json/wp/v2/` +} + +export function createWordPressClient( + extraArgs: Record | undefined, +): AxiosInstance { + const config = resolveWordPressConfig(extraArgs, appConfig) + const auth = Buffer.from( + `${config.wordpressUsername}:${config.wordpressPassword}`, + ).toString('base64') + + return axios.create({ + baseURL: normalizeWordPressBaseUrl(config.wordpressUrl), + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${auth}`, + }, + }) +} +``` + +## 11. Example FastMCP Tool Wiring + +```ts +server.addTool({ + name: 'list_content', + description: 'Lists WordPress content for the configured site.', + parameters: z.object({ + content_type: z.string().min(1), + page: z.number().optional(), + per_page: z.number().min(1).max(100).optional(), + }), + execute: async (args, context) => { + const client = createWordPressClient(context.extraArgs) + const response = await client.get(args.content_type, { + params: { + page: args.page, + per_page: args.per_page, + }, + }) + + return JSON.stringify(response.data, null, 2) + }, +}) +``` + +## 12. Validation Requirements + +## 12.1 Required Unit Tests + +Add tests covering: + +1. hidden config overrides env fallback +2. missing hidden/env config fails deterministically +3. wrong-type hidden args fail deterministically +4. hidden keys do not appear in the public tool schema +5. `site-management` removal or replacement works as intended + +## 12.2 Required Static Audit + +Search the entire package for the chosen hidden keys: + +- `wordpressUrl` +- `wordpressUsername` +- `wordpressPassword` +- `wordpressSqlEndpoint` + +They must not appear as public tool schema fields. + +## 12.3 Required Manual Validation + +1. Register the server in MissionSquad with `secretNames`. +2. Save per-user WordPress secrets. +3. Call at least one read tool and one write tool. +4. Confirm the tool works without any public auth fields. +5. Confirm the server can also run locally with env fallback. + +## 13. Acceptance Checklist + +- [ ] The package uses FastMCP or an equivalent runtime that exposes hidden args cleanly +- [ ] WordPress auth is resolved from hidden args first, env second +- [ ] The server starts without requiring credentials at process boot +- [ ] No global singleton client is keyed only to env config +- [ ] Hidden key names do not collide with any public schema fields +- [ ] Site-management tools no longer expose usernames or hidden site inventories +- [ ] Phase 1 MissionSquad mode is single-site per user +- [ ] README and example config are updated +- [ ] Tests cover hidden config resolution + +## 14. Final Recommendation + +Do not attempt a minimal patch that keeps the current raw SDK server, startup auth initialization, global `SiteManager`, and env-indexed multi-site model. + +That would preserve the exact assumptions that make the package incompatible with MissionSquad hidden secret injection. + +The correct refactor is: + +1. migrate to FastMCP +2. move auth/config resolution to request time +3. use hidden keys `wordpressUrl`, `wordpressUsername`, and `wordpressPassword` +4. scope MissionSquad v1 to one WordPress site per user +5. treat multi-site support as a later, separate product decision diff --git a/README.md b/README.md index 5f70c9f..f9302c2 100644 --- a/README.md +++ b/README.md @@ -1,308 +1,205 @@ # WordPress MCP Server -This is a Model Context Protocol (MCP) server for WordPress, allowing you to interact with your WordPress site using natural language via an MCP-compatible client like Claude for Desktop. This server exposes various WordPress data and functionality as MCP tools. +`@missionsquad/mcp-wp` is a stdio MCP server for interacting with WordPress through the WordPress REST API. -## Usage +This package now supports two runtime modes: -### Claude Desktop +- MissionSquad hidden-secret mode: per-call hidden credentials injected by MissionSquad +- local standalone mode: single-site env fallback -1. Download and install [Claude Desktop](https://claude.ai/download). -2. Open Claude Desktop settings and navigate to the "Developer" tab. -3. Copy the contents of the `claude_desktop_config.json.example` file. -4. Click "Edit Config" to open the `claude_desktop_config.json` file. -5. Copy paste the contents of the example file into the config file. Make sure to replace the placeholder values with your actual values for the WordPress site. To generate the application keys, follow this guide - [Application Passwords](https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide#Getting-Credentials). -6. Save the configuration. -7. Restart Claude Desktop. +## Runtime Contract -## Features +### MissionSquad Hidden-Secret Mode -This server provides tools to interact with core WordPress data and supports **multi-site management** - manage multiple WordPress sites from a single MCP server instance. - -### **Multi-Site Management** (3 tools) -Manage multiple WordPress sites from a single MCP server: - -* `list_sites`: List all configured WordPress sites -* `get_site`: Get details about a specific site configuration -* `test_site`: Test connection to a specific WordPress site - -All content and taxonomy tools support an optional `site_id` parameter to target specific sites. - -### **Unified Content Management** (8 tools) -Handles ALL content types (posts, pages, custom post types) with a single set of intelligent tools: - -* `list_content`: List any content type with filtering and pagination -* `get_content`: Get specific content by ID and type -* `create_content`: Create new content of any type -* `update_content`: Update existing content of any type -* `delete_content`: Delete content of any type -* `discover_content_types`: Find all available content types on your site -* `find_content_by_url`: Smart URL resolver that can find and optionally update content from any WordPress URL -* `get_content_by_slug`: Search by slug across all content types - -### **Unified Taxonomy Management** (8 tools) -Handles ALL taxonomies (categories, tags, custom taxonomies) with a single set of tools: - -* `discover_taxonomies`: Find all available taxonomies on your site -* `list_terms`: List terms in any taxonomy -* `get_term`: Get specific term by ID -* `create_term`: Create new term in any taxonomy -* `update_term`: Update existing term -* `delete_term`: Delete term from any taxonomy -* `assign_terms_to_content`: Assign terms to any content type -* `get_content_terms`: Get all terms for any content - -### **Specialized Tools** - -* **Media:** - * `list_media`: List all media items (supports pagination and searching). - * `get_media`: Retrieve a specific media item by ID. - * `create_media`: Create a new media item from a URL. - * `update_media`: Update an existing media item. - * `delete_media`: Delete a media item. -* **Users:** - * `list_users`: List all users with filtering, sorting, and pagination options. - * `get_user`: Retrieve a specific user by ID. - * `create_user`: Create a new user. - * `update_user`: Update an existing user. - * `delete_user`: Delete a user. -* **Comments:** - * `list_comments`: List all comments with filtering, sorting, and pagination options. - * `get_comment`: Retrieve a specific comment by ID. - * `create_comment`: Create a new comment. - * `update_comment`: Update an existing comment. - * `delete_comment`: Delete a comment. -* **Plugins:** - * `list_plugins`: List all plugins installed on the site. - * `get_plugin`: Retrieve details about a specific plugin. - * `activate_plugin`: Activate a plugin. - * `deactivate_plugin`: Deactivate a plugin. - * `create_plugin`: Create a new plugin. -* **Plugin Repository:** - * `search_plugins`: Search for plugins in the WordPress.org repository. - * `get_plugin_info`: Get detailed information about a plugin from the repository. - -### **Key Advantages** - -#### Smart URL Resolution -The `find_content_by_url` tool can: -- Take any WordPress URL and automatically find the corresponding content -- Detect content types from URL patterns (e.g., `/documentation/` → documentation custom post type) -- Optionally update the content in a single operation -- Works with posts, pages, and any custom post types - -#### Universal Content Operations -All content operations use a single `content_type` parameter: -```json -{ - "content_type": "post", // for blog posts - "content_type": "page", // for static pages - "content_type": "product", // for WooCommerce products - "content_type": "documentation" // for custom post types -} -``` +MissionSquad should register this server with hidden `secretNames` and inject them per tool call. -#### Universal Taxonomy Operations -All taxonomy operations use a single `taxonomy` parameter: -```json -{ - "taxonomy": "category", // for categories - "taxonomy": "post_tag", // for tags - "taxonomy": "product_category", // for WooCommerce - "taxonomy": "skill" // for custom taxonomies -} -``` +Recommended hidden keys: + +- `siteUrl` +- `username` +- `password` +- `sqlEndpoint` (optional) -## Configuration +These values are intentionally not part of the public tool schema. -### Single Site Configuration +The server reads them from FastMCP `context.extraArgs`. -For managing a single WordPress site, use the following environment variables: +### Local Standalone Mode + +For local usage outside MissionSquad, configure a single site with: ```env WORDPRESS_API_URL=https://your-wordpress-site.com WORDPRESS_USERNAME=wp_username WORDPRESS_PASSWORD=wp_app_password +WORDPRESS_SQL_ENDPOINT=/mcp/v1/query ``` -### Multi-Site Configuration +Only one fallback site is supported in env mode. -To manage multiple WordPress sites from a single MCP server, use numbered environment variables: +Legacy numbered `WORDPRESS_N_*` multi-site env configuration is no longer supported by this runtime. -```env -# Site 1 (Production) -WORDPRESS_1_URL=https://production-site.com -WORDPRESS_1_USERNAME=admin -WORDPRESS_1_PASSWORD=app_password_1 -WORDPRESS_1_ID=production -WORDPRESS_1_DEFAULT=true -WORDPRESS_1_ALIASES=prod,main - -# Site 2 (Staging) -WORDPRESS_2_URL=https://staging-site.com -WORDPRESS_2_USERNAME=admin -WORDPRESS_2_PASSWORD=app_password_2 -WORDPRESS_2_ID=staging -WORDPRESS_2_ALIASES=stage,dev - -# Site 3 (Development) -WORDPRESS_3_URL=https://dev-site.com -WORDPRESS_3_USERNAME=admin -WORDPRESS_3_PASSWORD=app_password_3 -WORDPRESS_3_ID=development -``` +## Features -**Multi-Site Configuration Options:** -- `WORDPRESS_N_URL`: WordPress site URL (required) -- `WORDPRESS_N_USERNAME`: WordPress username (required) -- `WORDPRESS_N_PASSWORD`: WordPress application password (required) -- `WORDPRESS_N_ID`: Site identifier (optional, defaults to `siteN`) -- `WORDPRESS_N_DEFAULT`: Set to `true` to make this the default site (optional, first site is default) -- `WORDPRESS_N_ALIASES`: Comma-separated aliases for site detection (optional) +- FastMCP stdio server +- hidden per-call WordPress credentials for MissionSquad compatibility +- single-site env fallback for local usage +- unified content tools +- unified taxonomy tools +- media, users, comments, plugins, and plugin-repository tools +- optional SQL query tool with custom endpoint -The server supports up to 10 sites. When using multi-site configuration, all tools accept an optional `site_id` parameter to target specific sites. +## Tool Surface -## Using with npx and .env file +### Site -You can run this MCP server directly using npx without installing it globally: +- `list_sites` +- `get_site` +- `test_site` -```bash -npx -y @instawp/mcp-wp -``` +These now describe the current request-scoped site only. + +### Content + +- `list_content` +- `get_content` +- `create_content` +- `update_content` +- `delete_content` +- `discover_content_types` +- `find_content_by_url` +- `get_content_by_slug` -Make sure you have a `.env` file in your current directory with the configuration variables shown above. +### Taxonomies -## Development +- `discover_taxonomies` +- `list_terms` +- `get_term` +- `create_term` +- `update_term` +- `delete_term` +- `assign_terms_to_content` +- `get_content_terms` -### Prerequisites +### Media -* **Node.js and npm:** Ensure you have Node.js (version 18 or higher) and npm installed. -* **WordPress Site:** You need an active WordPress site with the REST API enabled. -* **WordPress API Authentication:** Set up authentication for the WordPress REST API. This typically requires an authentication plugin or method (like Application Passwords). -* **MCP Client:** You need an application that can communicate with the MCP Server. Currently, Claude Desktop is recommended. +- `list_media` +- `create_media` +- `edit_media` +- `delete_media` -### Installation and Setup +### Users -1. **Clone the Repository:** +- `list_users` +- `get_user` +- `create_user` +- `update_user` +- `delete_user` - ```bash - git clone - cd wordpress-mcp-server - ``` +### Comments -2. **Install Dependencies:** +- `list_comments` +- `get_comment` +- `create_comment` +- `update_comment` +- `delete_comment` - ```bash - npm install - ``` +### Plugins -3. **Create a `.env` file:** +- `list_plugins` +- `get_plugin` +- `activate_plugin` +- `deactivate_plugin` +- `create_plugin` - Create a `.env` file in the root of your project directory and add your WordPress API credentials. - - For a single site: - ```env - WORDPRESS_API_URL=https://your-wordpress-site.com - WORDPRESS_USERNAME=wp_username - WORDPRESS_PASSWORD=wp_app_password - ``` - - For multiple sites: - ```env - WORDPRESS_1_URL=https://site1.com - WORDPRESS_1_USERNAME=admin - WORDPRESS_1_PASSWORD=app_password_1 - WORDPRESS_1_ID=site1 - WORDPRESS_1_DEFAULT=true - - WORDPRESS_2_URL=https://site2.com - WORDPRESS_2_USERNAME=admin - WORDPRESS_2_PASSWORD=app_password_2 - WORDPRESS_2_ID=site2 - ``` +### Plugin Repository - Replace the placeholders with your actual values. +- `search_plugins` +- `get_plugin_info` -4. **Build the Server:** +### SQL - ```bash - npm run build - ``` +- `execute_sql_query` -5. **Configure Claude Desktop:** +## MissionSquad Registration Example + +```json +{ + "name": "mcp-wordpress", + "transportType": "stdio", + "command": "node", + "args": ["/absolute/path/to/build/server.js"], + "secretNames": ["siteUrl", "username", "password", "sqlEndpoint"], + "enabled": true +} +``` - * Open Claude Desktop settings and navigate to the "Developer" tab. - * Click "Edit Config" to open the `claude_desktop_config.json` file. - * Add a new server configuration under the `mcpServers` section. You will need to provide the **absolute** path to the `build/server.js` file and your WordPress environment variables. - * Save the configuration. +## Local Development -### Running the Server +### Install -Once you've configured Claude Desktop, the server should start automatically whenever Claude Desktop starts. +```bash +npm install +``` -You can also run the server directly from the command line for testing: +### Build ```bash -npm start +npm run build ``` -or in development mode: +### Test ```bash -npm run dev +npm test ``` -### Security +## GitHub Actions + +This package includes MissionSquad-standard GitHub workflows: -* **Never commit your API keys or secrets to version control.** -* **Use HTTPS for communication between the client and server.** -* **Validate all inputs received from the client to prevent injection attacks.** -* **Implement proper error handling and rate limiting.** +- PR build/test on pull request `opened` and `synchronize` +- npm publish on push to `main` -## Project Overview +The publish workflow targets the `@missionsquad` npm scope and publishes with: -### Architecture +```bash +npm publish --access public +``` -The server uses a **unified tool architecture** to reduce complexity: +### Run +```bash +npm start ``` -src/ -├── server.ts # MCP server entry point -├── wordpress.ts # WordPress REST API client -├── cli.ts # CLI interface -├── config/ -│ └── site-manager.ts # Multi-site management -├── types/ -│ └── wordpress-types.ts # TypeScript definitions -└── tools/ - ├── index.ts # Tool aggregation - ├── site-management.ts # Site management (3 tools) - ├── unified-content.ts # Universal content management (8 tools) - ├── unified-taxonomies.ts # Universal taxonomy management (8 tools) - ├── media.ts # Media management (~5 tools) - ├── users.ts # User management (~5 tools) - ├── comments.ts # Comment management (~5 tools) - ├── plugins.ts # Plugin management (~5 tools) - └── plugin-repository.ts # WordPress.org plugin search (~2 tools) + +### Dev Mode + +```bash +npm run dev ``` -### Key Features +## SQL Query Tool + +`execute_sql_query` is intended for a custom read-only SQL endpoint on the target WordPress site. + +Default endpoint: + +```text +/mcp/v1/query +``` -- **Multi-Site Support**: Manage multiple WordPress sites from a single MCP server instance -- **Smart URL Resolution**: Automatically detect content types from URLs and find corresponding content -- **Universal Content Management**: Single set of tools handles posts, pages, and custom post types -- **Universal Taxonomy Management**: Single set of tools handles categories, tags, and custom taxonomies -- **Type Safety**: Full TypeScript support with Zod schema validation -- **Comprehensive Logging**: Detailed API request/response logging for debugging -- **Error Handling**: Graceful error handling with informative messages +Override order: -### Getting Started +1. hidden `sqlEndpoint` +2. `WORDPRESS_SQL_ENDPOINT` +3. default `/mcp/v1/query` -1. Clone the repository and install dependencies with `npm install` -2. Create a `.env` file with your WordPress credentials -3. Build the project with `npm run build` -4. Configure Claude Desktop with the server -5. Start using natural language to manage your WordPress site! +Only read-only queries are allowed. -### Contribution +## Security -Feel free to open issues or make pull requests to improve this project. Check out `CLAUDE.md` for detailed development guidelines. +- do not expose `siteUrl`, `username`, `password`, or `sqlEndpoint` in public tool schemas +- do not log hidden runtime config +- use HTTPS for WordPress sites +- use WordPress application passwords instead of primary login credentials diff --git a/claude_desktop_config.json.example b/claude_desktop_config.json.example index fc12d4e..e4d851a 100644 --- a/claude_desktop_config.json.example +++ b/claude_desktop_config.json.example @@ -2,27 +2,13 @@ "mcpServers": { "wordpress": { "command": "npx", - "args": ["-y", "@instawp/mcp-wp"], + "args": ["-y", "@missionsquad/mcp-wp"], "env": { - "_comment_single_site": "Single site configuration (use this OR multi-site, not both)", - "WORDPRESS_API_URL": "https://wpsite.instawp.xyz", + "WORDPRESS_API_URL": "https://wpsite.example.com", "WORDPRESS_USERNAME": "username", - "WORDPRESS_PASSWORD": "Application Password", - - "_comment_multi_site": "Multi-site configuration (up to 10 sites supported)", - "_WORDPRESS_1_URL": "https://production-site.com", - "_WORDPRESS_1_USERNAME": "admin", - "_WORDPRESS_1_PASSWORD": "app_password_1", - "_WORDPRESS_1_ID": "production", - "_WORDPRESS_1_DEFAULT": "true", - "_WORDPRESS_1_ALIASES": "prod,main", - - "_WORDPRESS_2_URL": "https://staging-site.com", - "_WORDPRESS_2_USERNAME": "admin", - "_WORDPRESS_2_PASSWORD": "app_password_2", - "_WORDPRESS_2_ID": "staging", - "_WORDPRESS_2_ALIASES": "stage,dev" + "WORDPRESS_PASSWORD": "application-password", + "WORDPRESS_SQL_ENDPOINT": "/mcp/v1/query" } } } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d19e3d7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4390 @@ +{ + "name": "@missionsquad/mcp-wp", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@missionsquad/mcp-wp", + "version": "0.1.0", + "license": "GPL-3.0-or-later", + "dependencies": { + "@missionsquad/fastmcp": "^1.1.3", + "@modelcontextprotocol/sdk": "^1.4.1", + "axios": "^1.6.7", + "dotenv": "^16.4.5", + "form-data": "^4.0.4", + "fs-extra": "^11.2.0", + "marked": "^17.0.3", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "bin": { + "mcp-wp": "build/server.js" + }, + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "@types/node": "^22.10.0", + "rimraf": "^5.0.5", + "tsx": "^4.7.1", + "typescript": "^5.3.3", + "vitest": "^2.1.8" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.13", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz", + "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "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": "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" + } + }, + "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/@missionsquad/fastmcp": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@missionsquad/fastmcp/-/fastmcp-1.1.3.tgz", + "integrity": "sha512-YX87Q72GXZgBx/LxXXRWy574E6sGjwYWQhGNpk4SD+Dy99Ey4k3cb5TAJANLcV3DFGxRFlmptrdiCaOcxf0kzg==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "execa": "^9.5.2", + "file-type": "^20.3.0", + "fuse.js": "^7.1.0", + "mcp-proxy": "^2.10.4", + "strict-event-emitter-types": "^2.0.0", + "tslib": "^2.8.1", + "undici": "^7.4.0", + "uri-templates": "^0.2.0", + "yargs": "^17.7.2", + "zod": "^3.24.2", + "zod-to-json-schema": "^3.24.3" + }, + "bin": { + "fastmcp": "dist/bin/fastmcp.js" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "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/@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", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "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/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", + "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "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/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "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/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/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz", + "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "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/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.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "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/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/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==", + "license": "MIT" + }, + "node_modules/cliui/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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "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.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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==", + "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/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "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/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/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": { + "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==", + "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.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/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/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/execa": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", + "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "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": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "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/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "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": { + "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": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "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": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "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" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fuse.js": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.3.0.tgz", + "integrity": "sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w==", + "license": "Apache-2.0", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/krisk" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "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" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.12", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz", + "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "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": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "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/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "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", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/marked": { + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.6.tgz", + "integrity": "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mcp-proxy": { + "version": "2.14.3", + "resolved": "https://registry.npmjs.org/mcp-proxy/-/mcp-proxy-2.14.3.tgz", + "integrity": "sha512-2jmpBclH72z5ViXhdVWopKDLAyhnFGyIhR7MrjE3BLzgYv3pZwVMHKLOogu1aIWZN/J4OrlZp0jraHKjEPD1RQ==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.11.4", + "eventsource": "^4.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "mcp-proxy": "dist/bin/mcp-proxy.js" + } + }, + "node_modules/mcp-proxy/node_modules/eventsource": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.1.0.tgz", + "integrity": "sha512-2GuF51iuHX6A9xdTccMTsNb7VO0lHZihApxhvQzJB5A03DvHDd2FQepodbMaztPBmBcE/ox7o2gqaxGhYB9LhQ==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=20.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", + "engines": { + "node": ">= 0.8" + } + }, + "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": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/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": ">= 0.6" + } + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/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/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-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": { + "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/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "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", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/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": ">=16.20.0" + } + }, + "node_modules/postcss": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "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": ">= 18" + } + }, + "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/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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "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==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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": "MIT" + }, + "node_modules/strict-event-emitter-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", + "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==", + "license": "ISC" + }, + "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/sindresorhus" + } + }, + "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": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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", + "engines": { + "node": ">=8" + } + }, + "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": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=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": ">=8" + } + }, + "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", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "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/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/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", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.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": ">=0.6" + } + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/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": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz", + "integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "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/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "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/uri-templates": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uri-templates/-/uri-templates-0.2.0.tgz", + "integrity": "sha512-EWkjYEN0L6KOfEoOH6Wj4ghQqU7eBZMJqRHQnxQAq+dSEzRPClkWjf8557HkWQXF6BrAUoLSAyy9i3RVTliaNg==", + "license": "http://geraintluff.github.io/tv4/LICENSE.txt" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.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", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "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" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 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": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=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": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/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": { + "node": ">=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": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=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": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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==", + "license": "MIT" + }, + "node_modules/yargs/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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/package.json b/package.json index 6b48ffc..550d700 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@instawp/mcp-wp", - "version": "0.0.3", + "name": "@missionsquad/mcp-wordpress", + "version": "0.1.0", "description": "A Model Context Protocol server for interacting with WordPress.", "type": "module", "main": "./build/server.js", @@ -16,6 +16,8 @@ "start": "node ./build/server.js", "dev": "tsx watch src/server.ts", "clean": "rimraf build", + "test": "vitest run", + "test:watch": "vitest", "prepare": "npm run build" }, "keywords": [ @@ -29,9 +31,11 @@ "author": "Claude", "license": "GPL-3.0-or-later", "dependencies": { + "@missionsquad/fastmcp": "^1.1.3", "@modelcontextprotocol/sdk": "^1.4.1", "axios": "^1.6.7", "dotenv": "^16.4.5", + "form-data": "^4.0.4", "fs-extra": "^11.2.0", "marked": "^17.0.3", "zod": "^3.23.8", @@ -42,7 +46,8 @@ "@types/node": "^22.10.0", "rimraf": "^5.0.5", "tsx": "^4.7.1", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "vitest": "^2.1.8" }, "publishConfig": { "access": "public" diff --git a/src/cli.ts b/src/cli.ts index a9dd3f3..ac7f23b 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -11,7 +11,7 @@ const __dirname = path.dirname(__filename); // Function to check if required environment variables are set function checkEnvironmentVariables() { - const requiredVars = ['WORDPRESS_API_URL', 'WORDPRESS_USERNAME', 'WORDPRESS_APP_PASSWORD']; + const requiredVars = ['WORDPRESS_API_URL', 'WORDPRESS_USERNAME', 'WORDPRESS_PASSWORD']; const missingVars = requiredVars.filter(varName => !process.env[varName]); if (missingVars.length > 0) { diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..871868c --- /dev/null +++ b/src/config.ts @@ -0,0 +1,141 @@ +import { UserError } from '@missionsquad/fastmcp' +import dotenv from 'dotenv' +import { z } from 'zod' + +dotenv.config() + +const DEFAULT_SQL_ENDPOINT = '/mcp/v1/query' + +const EnvSchema = z.object({ + WORDPRESS_API_URL: z.string().optional(), + WORDPRESS_USERNAME: z.string().optional(), + WORDPRESS_PASSWORD: z.string().optional(), + WORDPRESS_SQL_ENDPOINT: z.string().optional(), +}) + +export interface SingleSiteCredentials { + siteUrl: string + username: string + password: string +} + +export interface AppConfig { + fallbackSite?: SingleSiteCredentials + defaultSqlEndpoint: string +} + +export interface ResolvedWordPressRequestConfig { + site: SingleSiteCredentials + sqlEndpoint: string +} + +function readOptionalEnvString(value: string | undefined): string | undefined { + const trimmed = value?.trim() + return trimmed && trimmed.length > 0 ? trimmed : undefined +} + +function hasAnyLegacyMultiSiteEnv(source: NodeJS.ProcessEnv): boolean { + return Object.keys(source).some((key) => /^WORDPRESS_\d+_/.test(key)) +} + +function normalizeSiteUrl(rawUrl: string): string { + const trimmed = rawUrl.trim() + if (trimmed.length === 0) { + throw new UserError('WordPress site URL must be a non-empty string.') + } + + let parsed: URL + try { + parsed = new URL(trimmed) + } catch { + throw new UserError(`WordPress site URL is invalid: ${trimmed}`) + } + + parsed.search = '' + parsed.hash = '' + return parsed.toString().replace(/\/$/, '') +} + +function readHiddenString( + extraArgs: Record | undefined, + key: string, +): string | undefined { + const value = extraArgs?.[key] + if (value === undefined) { + return undefined + } + + if (typeof value !== 'string') { + throw new UserError(`Hidden argument "${key}" must be a string when provided.`) + } + + const trimmed = value.trim() + if (trimmed.length === 0) { + throw new UserError(`Hidden argument "${key}" must be a non-empty string when provided.`) + } + + return trimmed +} + +export function createAppConfigFromEnv(source: NodeJS.ProcessEnv): AppConfig { + const parsedEnv = EnvSchema.parse(source) + + if (hasAnyLegacyMultiSiteEnv(source)) { + throw new UserError( + 'Legacy multi-site WORDPRESS_N_* environment variables are no longer supported in this runtime. ' + + 'Use hidden per-call credentials in MissionSquad or configure single-site fallback with ' + + 'WORDPRESS_API_URL, WORDPRESS_USERNAME, and WORDPRESS_PASSWORD.' + ) + } + + const fallbackSiteUrl = readOptionalEnvString(parsedEnv.WORDPRESS_API_URL) + const fallbackUsername = readOptionalEnvString(parsedEnv.WORDPRESS_USERNAME) + const fallbackPassword = readOptionalEnvString(parsedEnv.WORDPRESS_PASSWORD) + const providedFallbackFieldCount = [fallbackSiteUrl, fallbackUsername, fallbackPassword].filter(Boolean).length + + if (providedFallbackFieldCount > 0 && providedFallbackFieldCount < 3) { + throw new UserError( + 'Single-site env fallback must provide WORDPRESS_API_URL, WORDPRESS_USERNAME, and WORDPRESS_PASSWORD together.' + ) + } + + return { + fallbackSite: + providedFallbackFieldCount === 3 + ? { + siteUrl: normalizeSiteUrl(fallbackSiteUrl!), + username: fallbackUsername!, + password: fallbackPassword!, + } + : undefined, + defaultSqlEndpoint: readOptionalEnvString(parsedEnv.WORDPRESS_SQL_ENDPOINT) ?? DEFAULT_SQL_ENDPOINT, + } +} + +export const appConfig: AppConfig = createAppConfigFromEnv(process.env) + +export function resolveWordPressRequestConfig( + extraArgs: Record | undefined, + defaults: AppConfig = appConfig, +): ResolvedWordPressRequestConfig { + const siteUrl = readHiddenString(extraArgs, 'siteUrl') ?? defaults.fallbackSite?.siteUrl + const username = readHiddenString(extraArgs, 'username') ?? defaults.fallbackSite?.username + const password = readHiddenString(extraArgs, 'password') ?? defaults.fallbackSite?.password + const sqlEndpoint = readHiddenString(extraArgs, 'sqlEndpoint') ?? defaults.defaultSqlEndpoint + + if (!siteUrl || !username || !password) { + throw new UserError( + 'WordPress credentials are required. Provide hidden arguments "siteUrl", "username", and "password", ' + + 'or configure the single-site env fallback with WORDPRESS_API_URL, WORDPRESS_USERNAME, and WORDPRESS_PASSWORD.' + ) + } + + return { + site: { + siteUrl: normalizeSiteUrl(siteUrl), + username, + password, + }, + sqlEndpoint, + } +} diff --git a/src/config/site-manager.ts b/src/config/site-manager.ts deleted file mode 100644 index abd627d..0000000 --- a/src/config/site-manager.ts +++ /dev/null @@ -1,241 +0,0 @@ -import axios, { AxiosInstance } from 'axios'; -import { logToFile } from '../wordpress.js'; - -export interface SiteConfig { - id: string; - url: string; - username: string; - password: string; - aliases?: string[]; - default?: boolean; -} - -export class SiteManager { - private sites = new Map(); - private clients = new Map(); - private defaultSiteId: string | null = null; - private initialized = false; - - constructor() { - // Don't load sites immediately - wait for first access - } - - /** - * Ensure sites are loaded (lazy initialization) - */ - private ensureInitialized() { - if (!this.initialized) { - this.loadSitesFromEnvironment(); - this.initialized = true; - } - } - - /** - * Load site configurations from environment variables - */ - private loadSitesFromEnvironment() { - let sitesFound = 0; - - // Check for numbered multi-site configuration (WORDPRESS_1_URL, WORDPRESS_2_URL, etc.) - for (let i = 1; i <= 10; i++) { // Support up to 10 sites - const urlKey = `WORDPRESS_${i}_URL`; - const usernameKey = `WORDPRESS_${i}_USERNAME`; - const passwordKey = `WORDPRESS_${i}_PASSWORD`; - const idKey = `WORDPRESS_${i}_ID`; - const aliasesKey = `WORDPRESS_${i}_ALIASES`; - const defaultKey = `WORDPRESS_${i}_DEFAULT`; - - if (process.env[urlKey] && process.env[usernameKey] && process.env[passwordKey]) { - const siteConfig: SiteConfig = { - id: process.env[idKey] || `site${i}`, - url: process.env[urlKey]!, - username: process.env[usernameKey]!, - password: process.env[passwordKey]!, - aliases: process.env[aliasesKey] ? process.env[aliasesKey]!.split(',').map(s => s.trim()) : undefined, - default: process.env[defaultKey] === 'true' || (sitesFound === 0 && i === 1) // First site is default unless explicitly set - }; - - this.sites.set(siteConfig.id, siteConfig); - if (siteConfig.default) { - this.defaultSiteId = siteConfig.id; - } - sitesFound++; - } - } - - // If no numbered sites found, fall back to single-site configuration - if (sitesFound === 0 && process.env.WORDPRESS_API_URL && process.env.WORDPRESS_USERNAME && process.env.WORDPRESS_PASSWORD) { - const siteConfig: SiteConfig = { - id: 'default', - url: process.env.WORDPRESS_API_URL, - username: process.env.WORDPRESS_USERNAME, - password: process.env.WORDPRESS_PASSWORD, - default: true - }; - this.sites.set('default', siteConfig); - this.defaultSiteId = 'default'; - sitesFound = 1; - logToFile('Loaded single site configuration from legacy environment variables'); - } - - if (sitesFound > 0) { - logToFile(`Loaded ${sitesFound} WordPress site(s) from environment variables`); - if (this.defaultSiteId) { - logToFile(`Default site: ${this.defaultSiteId}`); - } - } else { - throw new Error('No WordPress configuration found. Set WORDPRESS_1_URL, WORDPRESS_1_USERNAME, WORDPRESS_1_PASSWORD (and optionally WORDPRESS_2_*, etc.) or use legacy WORDPRESS_API_URL variables.'); - } - } - - /** - * Get site configuration by ID - */ - getSite(siteId?: string): SiteConfig { - this.ensureInitialized(); - - const targetSiteId = siteId || this.defaultSiteId; - if (!targetSiteId) { - throw new Error('No site specified and no default site configured'); - } - - const site = this.sites.get(targetSiteId); - if (!site) { - const availableSites = Array.from(this.sites.keys()).join(', '); - throw new Error(`Site '${targetSiteId}' not found. Available sites: ${availableSites}`); - } - - return site; - } - - /** - * Get all configured sites - */ - getAllSites(): SiteConfig[] { - this.ensureInitialized(); - return Array.from(this.sites.values()); - } - - /** - * Get default site ID - */ - getDefaultSiteId(): string | null { - this.ensureInitialized(); - return this.defaultSiteId; - } - - /** - * Detect site from context (domain mentions, aliases, etc.) - */ - detectSiteFromContext(requestText: string): string | null { - this.ensureInitialized(); - - if (!requestText) return null; - - const lowerRequest = requestText.toLowerCase(); - - // Check for domain mentions - for (const site of this.sites.values()) { - try { - const hostname = new URL(site.url).hostname; - if (lowerRequest.includes(hostname)) { - logToFile(`Detected site '${site.id}' from domain mention: ${hostname}`); - return site.id; - } - } catch (error) { - // Invalid URL, skip - } - } - - // Check for alias mentions - for (const site of this.sites.values()) { - if (site.aliases) { - for (const alias of site.aliases) { - if (lowerRequest.includes(alias.toLowerCase())) { - logToFile(`Detected site '${site.id}' from alias mention: ${alias}`); - return site.id; - } - } - } - } - - // Check for site ID mentions - for (const siteId of this.sites.keys()) { - if (lowerRequest.includes(siteId.toLowerCase())) { - logToFile(`Detected site '${siteId}' from ID mention`); - return siteId; - } - } - - return null; - } - - /** - * Get WordPress client for a specific site - */ - async getClient(siteId?: string): Promise { - this.ensureInitialized(); - - const site = this.getSite(siteId); - - if (!this.clients.has(site.id)) { - const client = await this.createClient(site); - this.clients.set(site.id, client); - } - - return this.clients.get(site.id)!; - } - - /** - * Create authenticated WordPress client for a site - */ - private async createClient(site: SiteConfig): Promise { - // Ensure the API URL has the WordPress REST API path - let baseURL = site.url.endsWith('/') ? site.url : `${site.url}/`; - - if (!baseURL.includes('/wp-json/wp/v2')) { - baseURL = baseURL + 'wp-json/wp/v2/'; - } else if (!baseURL.endsWith('/')) { - baseURL = baseURL + '/'; - } - - const auth = Buffer.from(`${site.username}:${site.password}`).toString('base64'); - - const client = axios.create({ - baseURL, - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Basic ${auth}` - } - }); - - // Test the connection - try { - await client.get(''); - logToFile(`Successfully connected to site '${site.id}' at ${baseURL}`); - } catch (error: any) { - logToFile(`Failed to connect to site '${site.id}': ${error.message}`); - throw new Error(`Failed to connect to site '${site.id}': ${error.message}`); - } - - return client; - } - - /** - * Test connection to a specific site - */ - async testSite(siteId?: string): Promise<{ success: boolean; error?: string }> { - this.ensureInitialized(); - - try { - const client = await this.getClient(siteId); - await client.get(''); - return { success: true }; - } catch (error: any) { - return { success: false, error: error.message }; - } - } -} - -// Global site manager instance -export const siteManager = new SiteManager(); diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..5bda6a1 --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,14 @@ +import { UserError } from '@missionsquad/fastmcp' + +export function toUserError(error: unknown, prefix: string): UserError { + if (error instanceof UserError) { + return error + } + + if (error instanceof Error) { + return new UserError(`${prefix}: ${error.message}`) + } + + return new UserError(`${prefix}: ${String(error)}`) +} + diff --git a/src/request-context.ts b/src/request-context.ts new file mode 100644 index 0000000..5b95b93 --- /dev/null +++ b/src/request-context.ts @@ -0,0 +1,43 @@ +import { AsyncLocalStorage } from 'node:async_hooks' +import type { AxiosInstance } from 'axios' +import { appConfig, resolveWordPressRequestConfig, type ResolvedWordPressRequestConfig } from './config.js' + +interface RequestContextState { + extraArgs?: Record + config?: ResolvedWordPressRequestConfig + client?: AxiosInstance +} + +const requestContextStorage = new AsyncLocalStorage() + +function getState(): RequestContextState { + const state = requestContextStorage.getStore() + if (!state) { + throw new Error('WordPress request context is not initialized for this tool call.') + } + return state +} + +export async function runWithRequestContext( + extraArgs: Record | undefined, + callback: () => Promise, +): Promise { + return await requestContextStorage.run({ extraArgs }, callback) +} + +export function getCurrentRequestConfig(): ResolvedWordPressRequestConfig { + const state = getState() + if (!state.config) { + state.config = resolveWordPressRequestConfig(state.extraArgs, appConfig) + } + return state.config +} + +export function getCurrentRequestClient(): AxiosInstance | undefined { + return getState().client +} + +export function setCurrentRequestClient(client: AxiosInstance): void { + getState().client = client +} + diff --git a/src/server.ts b/src/server.ts index 90898fe..c13c280 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,104 +1,89 @@ #!/usr/bin/env node -// src/server.ts -import * as dotenv from 'dotenv'; -dotenv.config(); // Load environment variables from .env first - -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { allTools, toolHandlers } from './tools/index.js'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; - - -// Create MCP server instance -const server = new McpServer({ - name: "wordpress", - version: "0.0.1" -}, { - capabilities: { - tools: allTools.reduce((acc, tool) => { - acc[tool.name] = tool; - return acc; - }, {} as Record) - } -}); - -// Register each tool from our tools list with its corresponding handler + +import { z } from 'zod' +import { FastMCP } from '@missionsquad/fastmcp' +import { allTools, toolHandlers } from './tools/index.js' +import { toUserError } from './errors.js' +import { runWithRequestContext } from './request-context.js' +import { routeConsoleStdoutToStderr } from './stdio-safe-console.js' +import { initWordPress } from './wordpress.js' + +routeConsoleStdoutToStderr() + +const server = new FastMCP({ + name: 'wordpress', + version: '0.0.3', +}) + +function extractToolText(result: unknown): string { + const toolResult = (result as { toolResult?: { content?: Array<{ text?: string }>; isError?: boolean } })?.toolResult + const text = toolResult?.content + ?.map((item) => item.text) + .filter((value): value is string => typeof value === 'string' && value.length > 0) + .join('\n\n') + + if (toolResult?.isError) { + throw new Error(text || 'Tool execution failed.') + } + + return text || JSON.stringify(result, null, 2) +} + for (const tool of allTools) { - const handler = toolHandlers[tool.name as keyof typeof toolHandlers]; - if (!handler) continue; - - const wrappedHandler = async (args: any) => { - // The handler functions are already typed with their specific parameter types - const result = await handler(args); - return { - content: result.toolResult.content.map((item: { type: string; text: string }) => ({ - ...item, - type: "text" as const - })), - isError: result.toolResult.isError - }; - }; - - // console.log(`Registering tool: ${tool.name}`); - // console.log(`Input schema: ${JSON.stringify(tool.inputSchema)}`); - - // const zodSchema = z.any().optional(); - // const jsonSchema = zodToJsonSchema(z.object(tool.inputSchema.properties as z.ZodRawShape)); - - // const schema = z.object(tool.inputSchema as z.ZodRawShape).catchall(z.unknown()); - - // The inputSchema is already in JSON Schema format with properties - // server.tool(tool.name, tool.inputSchema.shape, wrappedHandler); - // const zodSchema = z.any().optional(); - // const jsonSchema = zodToJsonSchema(z.object(tool.inputSchema.properties as z.ZodRawShape)); - // const parsedSchema = z.any().optional().parse(jsonSchema); - - const zodSchema = z.object(tool.inputSchema.properties as z.ZodRawShape); - server.tool(tool.name, zodSchema.shape, wrappedHandler) + const handler = toolHandlers[tool.name as keyof typeof toolHandlers] + if (!handler) { + continue + } + + const parameters = z.object(tool.inputSchema.properties as z.ZodRawShape) + server.addTool({ + name: tool.name, + description: tool.description ?? '', + parameters, + execute: async (args, context) => { + try { + return await runWithRequestContext(context.extraArgs, async () => { + const result = await (handler as (params: unknown) => Promise)(args) + return extractToolText(result) + }) + } catch (error) { + throw toUserError(error, `Tool ${tool.name} failed`) + } + }, + }) } -async function main() { - const { logToFile } = await import('./wordpress.js'); - logToFile('Starting WordPress MCP server...'); - - try { - logToFile('Initializing WordPress client...'); - const { initWordPress } = await import('./wordpress.js'); - await initWordPress(); - logToFile('WordPress client initialized successfully.'); - - logToFile('Setting up server transport...'); - const transport = new StdioServerTransport(); - await server.connect(transport); - logToFile('WordPress MCP Server running on stdio'); - } catch (error) { - logToFile(`Failed to initialize server: ${error}`); - process.exit(1); - } +async function main(): Promise { + await initWordPress() + await server.start({ transportType: 'stdio' }) +} + +async function shutdown(exitCode: number): Promise { + try { + await server.stop() + } finally { + process.exit(exitCode) + } } -// Handle process signals and errors -// IMPORTANT: MCP uses stdout for JSON-RPC — never use console.log here -process.on('SIGTERM', () => { - process.stderr.write('[SHUTDOWN] Received SIGTERM, shutting down...\n'); - process.exit(0); -}); process.on('SIGINT', () => { - process.stderr.write('[SHUTDOWN] Received SIGINT, shutting down...\n'); - process.exit(0); -}); -process.on('uncaughtException', (error) => { - process.stderr.write(`[FATAL] Uncaught exception: ${error}\n`); - process.exit(1); -}); -process.on('unhandledRejection', (error) => { - process.stderr.write(`[FATAL] Unhandled rejection: ${error}\n`); - process.exit(1); -}); - -main().catch((error) => { - process.stderr.write(`[FATAL] Startup error: ${error}\n`); - process.exit(1); -}); \ No newline at end of file + void shutdown(0) +}) + +process.on('SIGTERM', () => { + void shutdown(0) +}) + +process.on('uncaughtException', () => { + void shutdown(1) +}) + +process.on('unhandledRejection', () => { + void shutdown(1) +}) + +void main().catch(() => { + void shutdown(1) +}) + diff --git a/src/stdio-safe-console.ts b/src/stdio-safe-console.ts new file mode 100644 index 0000000..e9b0d5e --- /dev/null +++ b/src/stdio-safe-console.ts @@ -0,0 +1,25 @@ +import { format, inspect, type InspectOptions } from 'node:util' + +function writeLineToStderr(line: string): void { + process.stderr.write(`${line}\n`) +} + +function writeArgsToStderr(...args: unknown[]): void { + writeLineToStderr(format(...args)) +} + +export function routeConsoleStdoutToStderr(): void { + console.log = (...args: unknown[]): void => { + writeArgsToStderr(...args) + } + console.info = (...args: unknown[]): void => { + writeArgsToStderr(...args) + } + console.debug = (...args: unknown[]): void => { + writeArgsToStderr(...args) + } + console.dir = (item: unknown, options?: InspectOptions): void => { + writeLineToStderr(inspect(item, options)) + } +} + diff --git a/src/tools/index.ts b/src/tools/index.ts index a49d896..6a5bab3 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -8,6 +8,7 @@ import { userTools, userHandlers } from './users.js'; import { pluginRepositoryTools, pluginRepositoryHandlers } from './plugin-repository.js'; import { commentTools, commentHandlers } from './comments.js'; import { siteManagementTools, siteManagementHandlers } from './site-management.js'; +import { sqlQueryTools, sqlQueryHandlers } from './sql-query.js'; // Combine all tools - now significantly reduced from ~65 to ~38 tools export const allTools: Tool[] = [ @@ -18,7 +19,8 @@ export const allTools: Tool[] = [ ...userTools, // ~5 tools ...pluginRepositoryTools, // ~2 tools ...commentTools, // ~5 tools - ...siteManagementTools // 3 tools (multi-site support) + ...siteManagementTools, // 3 tools (current request site support) + ...sqlQueryTools ]; // Combine all handlers @@ -30,5 +32,6 @@ export const toolHandlers = { ...userHandlers, ...pluginRepositoryHandlers, ...commentHandlers, - ...siteManagementHandlers -}; \ No newline at end of file + ...siteManagementHandlers, + ...sqlQueryHandlers +}; diff --git a/src/tools/site-management.ts b/src/tools/site-management.ts index 9b96c0b..c5abe2a 100644 --- a/src/tools/site-management.ts +++ b/src/tools/site-management.ts @@ -1,153 +1,157 @@ -// src/tools/site-management.ts -import { z } from 'zod'; -import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import { siteManager } from '../config/site-manager.js'; +import { z } from 'zod' +import { type Tool } from '@modelcontextprotocol/sdk/types.js' +import { getCurrentSiteSummary, testCurrentSiteConnection } from '../wordpress.js' -// Schemas -const listSitesSchema = z.object({}); +const emptySchema = z.object({}) -const getSiteSchema = z.object({ - site_id: z.string().optional().describe('Site ID to get details for. If not provided, returns the default site.') -}); - -const testSiteSchema = z.object({ - site_id: z.string().optional().describe('Site ID to test connection. If not provided, tests the default site.') -}); - -// Tools export const siteManagementTools: Tool[] = [ { name: 'list_sites', - description: 'List all configured WordPress sites. Shows site IDs, URLs, and which is the default.', + description: 'List the current WordPress site configuration available for this tool call.', inputSchema: { type: 'object', - properties: listSitesSchema.shape, - required: [] - } + properties: emptySchema.shape, + required: [], + }, }, { name: 'get_site', - description: 'Get details about a specific WordPress site configuration.', + description: 'Get details about the current WordPress site configuration for this tool call.', inputSchema: { type: 'object', - properties: getSiteSchema.shape, - required: [] - } + properties: emptySchema.shape, + required: [], + }, }, { name: 'test_site', - description: 'Test the connection to a specific WordPress site.', + description: 'Test the connection to the current WordPress site for this tool call.', inputSchema: { type: 'object', - properties: testSiteSchema.shape, - required: [] - } - } -]; + properties: emptySchema.shape, + required: [], + }, + }, +] -// Handlers export const siteManagementHandlers = { - list_sites: async (params: z.infer) => { + list_sites: async (_params: z.infer) => { try { - const sites = siteManager.getAllSites(); - const defaultSiteId = siteManager.getDefaultSiteId(); - - const sitesList = sites.map(site => ({ - id: site.id, - url: site.url, - username: site.username, - aliases: site.aliases || [], - isDefault: site.id === defaultSiteId - })); - + const site = getCurrentSiteSummary() return { toolResult: { - content: [{ - type: 'text' as const, - text: JSON.stringify({ - sites: sitesList, - count: sites.length, - default_site: defaultSiteId - }, null, 2) - }] - } - }; + content: [ + { + type: 'text' as const, + text: JSON.stringify( + { + sites: [ + { + id: site.id, + url: site.url, + isDefault: true, + }, + ], + count: 1, + default_site: site.id, + }, + null, + 2, + ), + }, + ], + }, + } } catch (error: any) { return { toolResult: { - content: [{ - type: 'text' as const, - text: `Error listing sites: ${error.message}` - }], - isError: true - } - }; + content: [ + { + type: 'text' as const, + text: `Error listing sites: ${error.message}`, + }, + ], + isError: true, + }, + } } }, - get_site: async (params: z.infer) => { + get_site: async (_params: z.infer) => { try { - const site = siteManager.getSite(params.site_id); - + const site = getCurrentSiteSummary() return { toolResult: { - content: [{ - type: 'text' as const, - text: JSON.stringify({ - id: site.id, - url: site.url, - username: site.username, - aliases: site.aliases || [], - isDefault: site.id === siteManager.getDefaultSiteId() - }, null, 2) - }] - } - }; + content: [ + { + type: 'text' as const, + text: JSON.stringify( + { + id: site.id, + url: site.url, + isDefault: true, + }, + null, + 2, + ), + }, + ], + }, + } } catch (error: any) { return { toolResult: { - content: [{ - type: 'text' as const, - text: `Error getting site: ${error.message}` - }], - isError: true - } - }; + content: [ + { + type: 'text' as const, + text: `Error getting site: ${error.message}`, + }, + ], + isError: true, + }, + } } }, - test_site: async (params: z.infer) => { + test_site: async (_params: z.infer) => { try { - const result = await siteManager.testSite(params.site_id); - const site = siteManager.getSite(params.site_id); - + const site = getCurrentSiteSummary() + const result = await testCurrentSiteConnection() return { toolResult: { - content: [{ - type: 'text' as const, - text: JSON.stringify({ - site_id: site.id, - site_url: site.url, - success: result.success, - error: result.error || null, - message: result.success - ? `Successfully connected to ${site.url}` - : `Failed to connect to ${site.url}: ${result.error}` - }, null, 2) - }], - isError: !result.success - } - }; + content: [ + { + type: 'text' as const, + text: JSON.stringify( + { + site_id: site.id, + site_url: site.url, + success: result.success, + error: result.error ?? null, + message: result.success + ? `Successfully connected to ${site.url}` + : `Failed to connect to ${site.url}: ${result.error}`, + }, + null, + 2, + ), + }, + ], + isError: !result.success, + }, + } } catch (error: any) { return { toolResult: { - content: [{ - type: 'text' as const, - text: `Error testing site: ${error.message}` - }], - isError: true - } - }; + content: [ + { + type: 'text' as const, + text: `Error testing site: ${error.message}`, + }, + ], + isError: true, + }, + } } - } -}; + }, +} diff --git a/src/tools/sql-query.ts b/src/tools/sql-query.ts index 37da1fa..14772ff 100644 --- a/src/tools/sql-query.ts +++ b/src/tools/sql-query.ts @@ -1,7 +1,7 @@ // src/tools/sql-query.ts import { z } from 'zod'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import { makeWordPressRequest } from '../wordpress.js'; +import { getCurrentSqlEndpoint, makeWordPressRequest } from '../wordpress.js'; // Schema for SQL query execution const executeSqlQuerySchema = z.object({ @@ -92,7 +92,7 @@ export const sqlQueryHandlers = { // Execute the query via the custom endpoint // Use environment variable or default to /mcp/v1/query - const sqlEndpoint = process.env.WORDPRESS_SQL_ENDPOINT || '/mcp/v1/query'; + const sqlEndpoint = getCurrentSqlEndpoint(); const response = await makeWordPressRequest( 'POST', sqlEndpoint, @@ -119,6 +119,13 @@ export const sqlQueryHandlers = { } catch (error: any) { // Check if it's a 404 error (endpoint not found) if (error.response?.status === 404) { + const expectedEndpoint = (() => { + try { + return getCurrentSqlEndpoint() + } catch { + return process.env.WORDPRESS_SQL_ENDPOINT || '/mcp/v1/query' + } + })() return { toolResult: { content: [{ @@ -127,8 +134,8 @@ export const sqlQueryHandlers = { To enable this feature, see the setup instructions in README.md under "Enabling SQL Query Tool (Optional)". -Expected endpoint: ${process.env.WORDPRESS_SQL_ENDPOINT || '/mcp/v1/query'} -You can customize this by setting the WORDPRESS_SQL_ENDPOINT environment variable.` +Expected endpoint: ${expectedEndpoint} +You can customize this by setting the hidden "sqlEndpoint" value or the WORDPRESS_SQL_ENDPOINT environment variable.` }], isError: true } diff --git a/src/tools/unified-content.ts b/src/tools/unified-content.ts index a3ed90d..b93c737 100644 --- a/src/tools/unified-content.ts +++ b/src/tools/unified-content.ts @@ -1,27 +1,26 @@ // src/tools/unified-content.ts import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import { makeWordPressRequest, logToFile } from '../wordpress.js'; +import { getCurrentSiteCacheKey, makeWordPressRequest, logToFile } from '../wordpress.js'; import { z } from 'zod'; -// Cache for post types to reduce API calls -let postTypesCache: any = null; -let cacheTimestamp: number = 0; +const postTypesCache = new Map(); const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes // Helper function to get all post types with caching -async function getPostTypes(forceRefresh = false, siteId?: string) { +async function getPostTypes(forceRefresh = false) { const now = Date.now(); + const cacheKey = getCurrentSiteCacheKey(); + const cached = postTypesCache.get(cacheKey); - if (!forceRefresh && postTypesCache && (now - cacheTimestamp) < CACHE_DURATION) { + if (!forceRefresh && cached && (now - cached.timestamp) < CACHE_DURATION) { logToFile('Using cached post types'); - return postTypesCache; + return cached.value; } try { logToFile('Fetching post types from API'); - const response = await makeWordPressRequest('GET', 'types', undefined, { siteId }); - postTypesCache = response; - cacheTimestamp = now; + const response = await makeWordPressRequest('GET', 'types'); + postTypesCache.set(cacheKey, { value: response, timestamp: now }); return response; } catch (error: any) { logToFile(`Error fetching post types: ${error.message}`); @@ -62,12 +61,12 @@ function parseUrl(url: string): { slug: string; pathHints: string[] } { } // Helper function to find content across multiple post types -async function findContentAcrossTypes(slug: string, contentTypes?: string[], siteId?: string) { +async function findContentAcrossTypes(slug: string, contentTypes?: string[]) { const typesToSearch = contentTypes || []; // If no specific content types provided, get all available types if (typesToSearch.length === 0) { - const allTypes = await getPostTypes(false, siteId); + const allTypes = await getPostTypes(false); typesToSearch.push(...Object.keys(allTypes).filter(type => type !== 'attachment' && type !== 'wp_block' )); @@ -83,7 +82,7 @@ async function findContentAcrossTypes(slug: string, contentTypes?: string[], sit const response = await makeWordPressRequest('GET', endpoint, { slug: slug, per_page: 1 - }, { siteId }); + }); if (Array.isArray(response) && response.length > 0) { logToFile(`Found content with slug "${slug}" in content type "${contentType}"`); @@ -100,7 +99,6 @@ async function findContentAcrossTypes(slug: string, contentTypes?: string[], sit // Schema definitions const listContentSchema = z.object({ content_type: z.string().describe("The content type slug (e.g., 'post', 'page', 'product', 'documentation')"), - site_id: z.string().optional().describe("Site ID (for multi-site setups)"), page: z.number().optional().describe("Page number (default 1)"), per_page: z.number().min(1).max(100).optional().describe("Items per page (default 10, max 100)"), search: z.string().optional().describe("Search term for content title or body"), @@ -118,13 +116,11 @@ const listContentSchema = z.object({ const getContentSchema = z.object({ content_type: z.string().describe("The content type slug"), - id: z.number().describe("Content ID"), - site_id: z.string().optional().describe("Site ID (for multi-site setups)") + id: z.number().describe("Content ID") }); const createContentSchema = z.object({ content_type: z.string().describe("The content type slug"), - site_id: z.string().optional().describe("Site ID (for multi-site setups)"), title: z.string().describe("Content title"), content: z.string().describe("Content body"), status: z.string().optional().default('draft').describe("Content status"), @@ -144,7 +140,6 @@ const createContentSchema = z.object({ const updateContentSchema = z.object({ content_type: z.string().describe("The content type slug"), id: z.number().describe("Content ID"), - site_id: z.string().optional().describe("Site ID (for multi-site setups)"), title: z.string().optional().describe("Content title"), content: z.string().optional().describe("Content body"), status: z.string().optional().describe("Content status"), @@ -164,18 +159,15 @@ const updateContentSchema = z.object({ const deleteContentSchema = z.object({ content_type: z.string().describe("The content type slug"), id: z.number().describe("Content ID"), - site_id: z.string().optional().describe("Site ID (for multi-site setups)"), force: z.boolean().optional().describe("Whether to bypass trash and force deletion") }); const discoverContentTypesSchema = z.object({ - refresh_cache: z.boolean().optional().describe("Force refresh the content types cache"), - site_id: z.string().optional().describe("Site ID (for multi-site setups)") + refresh_cache: z.boolean().optional().describe("Force refresh the content types cache") }); const findContentByUrlSchema = z.object({ url: z.string().describe("The full URL of the content to find"), - site_id: z.string().optional().describe("Site ID (for multi-site setups)"), update_fields: z.object({ title: z.string().optional(), content: z.string().optional(), @@ -187,7 +179,6 @@ const findContentByUrlSchema = z.object({ const getContentBySlugSchema = z.object({ slug: z.string().describe("The slug to search for"), - site_id: z.string().optional().describe("Site ID (for multi-site setups)"), content_types: z.array(z.string()).optional().describe("Content types to search in (defaults to all)") }); @@ -248,9 +239,9 @@ export const unifiedContentHandlers = { list_content: async (params: ListContentParams) => { try { const endpoint = getContentEndpoint(params.content_type); - const { content_type, site_id, ...queryParams } = params; + const { content_type, ...queryParams } = params; - const response = await makeWordPressRequest('GET', endpoint, queryParams, { siteId: site_id }); + const response = await makeWordPressRequest('GET', endpoint, queryParams); return { toolResult: { @@ -277,7 +268,7 @@ export const unifiedContentHandlers = { get_content: async (params: GetContentParams) => { try { const endpoint = getContentEndpoint(params.content_type); - const response = await makeWordPressRequest('GET', `${endpoint}/${params.id}`, undefined, { siteId: params.site_id }); + const response = await makeWordPressRequest('GET', `${endpoint}/${params.id}`); return { toolResult: { @@ -337,7 +328,7 @@ export const unifiedContentHandlers = { } }); - const response = await makeWordPressRequest('POST', endpoint, contentData, { siteId: params.site_id }); + const response = await makeWordPressRequest('POST', endpoint, contentData); return { toolResult: { @@ -387,7 +378,7 @@ export const unifiedContentHandlers = { Object.assign(updateData, params.custom_fields); } - const response = await makeWordPressRequest('POST', `${endpoint}/${params.id}`, updateData, { siteId: params.site_id }); + const response = await makeWordPressRequest('POST', `${endpoint}/${params.id}`, updateData); return { toolResult: { @@ -417,7 +408,7 @@ export const unifiedContentHandlers = { const response = await makeWordPressRequest('DELETE', `${endpoint}/${params.id}`, { force: params.force || false - }, { siteId: params.site_id }); + }); return { toolResult: { @@ -443,7 +434,7 @@ export const unifiedContentHandlers = { discover_content_types: async (params: DiscoverContentTypesParams) => { try { - const contentTypes = await getPostTypes(params.refresh_cache || false, params.site_id); + const contentTypes = await getPostTypes(params.refresh_cache || false); // Format the response to be more readable const formattedTypes = Object.entries(contentTypes).map(([slug, type]: [string, any]) => ({ @@ -519,11 +510,11 @@ export const unifiedContentHandlers = { const typesToSearch = [...new Set(priorityTypes)]; // Find the content - const result = await findContentAcrossTypes(slug, typesToSearch, params.site_id); + const result = await findContentAcrossTypes(slug, typesToSearch); if (!result) { // If not found in priority types, search all types - const allResult = await findContentAcrossTypes(slug, undefined, params.site_id); + const allResult = await findContentAcrossTypes(slug, undefined); if (!allResult) { throw new Error(`No content found with URL: ${params.url}`); } @@ -543,7 +534,7 @@ export const unifiedContentHandlers = { Object.assign(updateData, params.update_fields.custom_fields); } - const updatedContent = await makeWordPressRequest('POST', `${endpoint}/${content.id}`, updateData, { siteId: params.site_id }); + const updatedContent = await makeWordPressRequest('POST', `${endpoint}/${content.id}`, updateData); return { toolResult: { @@ -595,7 +586,7 @@ export const unifiedContentHandlers = { Object.assign(updateData, params.update_fields.custom_fields); } - const updatedContent = await makeWordPressRequest('POST', `${endpoint}/${content.id}`, updateData, { siteId: params.site_id }); + const updatedContent = await makeWordPressRequest('POST', `${endpoint}/${content.id}`, updateData); return { toolResult: { @@ -645,7 +636,7 @@ export const unifiedContentHandlers = { get_content_by_slug: async (params: GetContentBySlugParams) => { try { - const result = await findContentAcrossTypes(params.slug, params.content_types, params.site_id); + const result = await findContentAcrossTypes(params.slug, params.content_types); if (!result) { throw new Error(`No content found with slug: ${params.slug}`); @@ -676,4 +667,4 @@ export const unifiedContentHandlers = { }; } } -}; \ No newline at end of file +}; diff --git a/src/tools/unified-taxonomies.ts b/src/tools/unified-taxonomies.ts index 4128101..833dac3 100644 --- a/src/tools/unified-taxonomies.ts +++ b/src/tools/unified-taxonomies.ts @@ -1,27 +1,26 @@ // src/tools/unified-taxonomies.ts import { Tool } from '@modelcontextprotocol/sdk/types.js'; -import { makeWordPressRequest, logToFile } from '../wordpress.js'; +import { getCurrentSiteCacheKey, makeWordPressRequest, logToFile } from '../wordpress.js'; import { z } from 'zod'; -// Cache for taxonomies to reduce API calls -let taxonomiesCache: any = null; -let taxonomyCacheTimestamp: number = 0; +const taxonomiesCache = new Map(); const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes // Helper function to get all taxonomies with caching async function getTaxonomies(forceRefresh = false) { const now = Date.now(); + const cacheKey = getCurrentSiteCacheKey(); + const cached = taxonomiesCache.get(cacheKey); - if (!forceRefresh && taxonomiesCache && (now - taxonomyCacheTimestamp) < CACHE_DURATION) { + if (!forceRefresh && cached && (now - cached.timestamp) < CACHE_DURATION) { logToFile('Using cached taxonomies'); - return taxonomiesCache; + return cached.value; } try { logToFile('Fetching taxonomies from API'); const response = await makeWordPressRequest('GET', 'taxonomies'); - taxonomiesCache = response; - taxonomyCacheTimestamp = now; + taxonomiesCache.set(cacheKey, { value: response, timestamp: now }); return response; } catch (error: any) { logToFile(`Error fetching taxonomies: ${error.message}`); @@ -526,4 +525,4 @@ export const unifiedTaxonomyHandlers = { }; } } -}; \ No newline at end of file +}; diff --git a/src/wordpress.ts b/src/wordpress.ts index 728874c..25076e2 100644 --- a/src/wordpress.ts +++ b/src/wordpress.ts @@ -1,133 +1,135 @@ -// src/wordpress.ts -import * as dotenv from 'dotenv'; -import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; -import { siteManager } from './config/site-manager.js'; - -// Legacy global WordPress API client instance for backward compatibility -let wpClient: AxiosInstance; - -/** - * Initialize the WordPress API client with authentication - * Now uses SiteManager for multi-site support - */ -export async function initWordPress() { - // Initialize the default site client - const client = await siteManager.getClient(); - wpClient = client; - logToFile('WordPress client initialized successfully via SiteManager'); +import axios, { type AxiosInstance, type AxiosResponse } from 'axios' +import { getCurrentRequestClient, getCurrentRequestConfig, setCurrentRequestClient } from './request-context.js' + +type HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT' + +interface RequestOptions { + headers?: Record + isFormData?: boolean + rawResponse?: boolean } -export function logToFile(message: string) { - // Logging disabled - return; +function buildBaseUrl(siteUrl: string): string { + const normalized = siteUrl.endsWith('/') ? siteUrl : `${siteUrl}/` + + if (!normalized.includes('/wp-json/wp/v2/')) { + if (normalized.includes('/wp-json/wp/v2')) { + return `${normalized}/` + } + return `${normalized}wp-json/wp/v2/` + } + + return normalized.endsWith('/') ? normalized : `${normalized}/` } -/** - * Make a request to the WordPress API - * @param method HTTP method - * @param endpoint API endpoint (relative to the baseURL) - * @param data Request data - * @param options Additional request options including siteId for multi-site support - * @returns Response data - */ -export async function makeWordPressRequest( - method: string, - endpoint: string, - data?: any, - options?: { - headers?: Record; - isFormData?: boolean; - rawResponse?: boolean; - siteId?: string; +function createWordPressClient(): AxiosInstance { + const { + site: { siteUrl, username, password }, + } = getCurrentRequestConfig() + const auth = Buffer.from(`${username}:${password}`).toString('base64') + + return axios.create({ + baseURL: buildBaseUrl(siteUrl), + headers: { + 'Content-Type': 'application/json', + Authorization: `Basic ${auth}`, + }, + }) +} + +function getWordPressClient(): AxiosInstance { + const existing = getCurrentRequestClient() + if (existing) { + return existing } -) { - // Get the appropriate client for the site - const client = options?.siteId - ? await siteManager.getClient(options.siteId) - : (wpClient || await siteManager.getClient()); - - // Log data (skip for FormData which can't be stringified) - if (!options?.isFormData) { - logToFile(`Data: ${JSON.stringify(data, null, 2)}`); - } else { - logToFile('Request contains FormData (not shown in logs)'); + + const client = createWordPressClient() + setCurrentRequestClient(client) + return client +} + +export async function initWordPress(): Promise { + // No startup connection is required. MissionSquad hidden secrets are resolved per tool call. +} + +export function logToFile(_message: string): void { + // Logging disabled +} + +export function getCurrentSiteSummary(): { id: 'current'; url: string; sqlEndpoint: string } { + const { site, sqlEndpoint } = getCurrentRequestConfig() + return { + id: 'current', + url: site.siteUrl, + sqlEndpoint, } - - // Handle potential leading slash in endpoint - const path = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; +} + +export function getCurrentSiteCacheKey(): string { + return getCurrentRequestConfig().site.siteUrl +} + +export function getCurrentSqlEndpoint(): string { + return getCurrentRequestConfig().sqlEndpoint +} +export async function testCurrentSiteConnection(): Promise<{ success: boolean; error?: string }> { try { - const fullUrl = `${client.defaults.baseURL}${path}`; - - // Prepare request config - const requestConfig: any = { - method, - url: path, - headers: options?.headers || {} - }; - - // Handle different data formats based on method and options - if (method === 'GET') { - requestConfig.params = data; - } else if (options?.isFormData) { - // For FormData, pass it directly without stringifying - requestConfig.data = data; - } else if (method === 'POST') { - requestConfig.data = JSON.stringify(data); - } else { - requestConfig.data = data; - } - - const requestLog = ` -REQUEST: -URL: ${fullUrl} -Method: ${method} -Site: ${options?.siteId || 'default'} -Headers: ${JSON.stringify({...client.defaults.headers, ...requestConfig.headers}, null, 2)} -Data: ${options?.isFormData ? '(FormData not shown)' : JSON.stringify(data, null, 2)} -`; - logToFile(requestLog); - - const response = await client.request(requestConfig); - - const responseLog = ` -RESPONSE: -Status: ${response.status} -Data: ${JSON.stringify(response.data, null, 2)} -`; - logToFile(responseLog); - - return options?.rawResponse ? response : response.data; + const client = getWordPressClient() + await client.get('') + return { success: true } } catch (error: any) { - const errorLog = ` -ERROR: -Message: ${error.message} -Status: ${error.response?.status || 'N/A'} -Data: ${JSON.stringify(error.response?.data || {}, null, 2)} -`; - logToFile(errorLog); - throw error; + return { success: false, error: error.message } } } -/** - * Make a request to the WordPress.org Plugin Repository API - * @param searchQuery Search query string - * @param page Page number (1-based) - * @param perPage Number of results per page - * @returns Response data from WordPress.org Plugin API - */ -export async function searchWordPressPluginRepository(searchQuery: string, page: number = 1, perPage: number = 10) { - try { - // WordPress.org Plugin API endpoint - const apiUrl = 'https://api.wordpress.org/plugins/info/1.2/'; - - // Build the request data according to WordPress.org Plugin API format - const requestData = { +export async function makeWordPressRequest( + method: HttpMethod, + endpoint: string, + data?: unknown, + options?: RequestOptions, +): Promise { + const client = getWordPressClient() + const path = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint + + const requestConfig: { + method: HttpMethod + url: string + headers: Record + params?: unknown + data?: unknown + } = { + method, + url: path, + headers: options?.headers ?? {}, + } + + if (method === 'GET') { + requestConfig.params = data + } else if (options?.isFormData) { + requestConfig.data = data + } else if (method === 'POST') { + requestConfig.data = JSON.stringify(data) + } else { + requestConfig.data = data + } + + const response = await client.request(requestConfig) + return options?.rawResponse ? response : response.data +} + +export async function searchWordPressPluginRepository( + searchQuery: string, + page = 1, + perPage = 10, +): Promise { + const response = await axios.post( + 'https://api.wordpress.org/plugins/info/1.2/', + { action: 'query_plugins', request: { search: searchQuery, - page: page, + page, per_page: perPage, fields: { description: true, @@ -140,41 +142,16 @@ export async function searchWordPressPluginRepository(searchQuery: string, page: downloadlink: true, last_updated: true, homepage: true, - tags: true - } - } - }; - - const requestLog = ` -WORDPRESS.ORG PLUGIN API REQUEST: -URL: ${apiUrl} -Data: ${JSON.stringify(requestData, null, 2)} -`; - logToFile(requestLog); - - const response = await axios.post(apiUrl, requestData, { + tags: true, + }, + }, + }, + { headers: { - 'Content-Type': 'application/json' - } - }); - - const responseLog = ` -WORDPRESS.ORG PLUGIN API RESPONSE: -Status: ${response.status} -Info: ${JSON.stringify(response.data.info, null, 2)} -Plugins Count: ${response.data.plugins?.length || 0} -`; - logToFile(responseLog); - - return response.data; - } catch (error: any) { - const errorLog = ` -WORDPRESS.ORG PLUGIN API ERROR: -Message: ${error.message} -Status: ${error.response?.status || 'N/A'} -Data: ${JSON.stringify(error.response?.data || {}, null, 2)} -`; - logToFile(errorLog); - throw error; - } -} \ No newline at end of file + 'Content-Type': 'application/json', + }, + }, + ) + + return response.data +} diff --git a/test/config.test.ts b/test/config.test.ts new file mode 100644 index 0000000..0b43ed5 --- /dev/null +++ b/test/config.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, it } from 'vitest' +import { + createAppConfigFromEnv, + resolveWordPressRequestConfig, + type AppConfig, +} from '../src/config.js' + +const TEST_DEFAULTS: AppConfig = { + fallbackSite: { + siteUrl: 'https://example.com', + username: 'fallback-user', + password: 'fallback-password', + }, + defaultSqlEndpoint: '/mcp/v1/query', +} + +describe('createAppConfigFromEnv', () => { + it('creates a single-site fallback when all required env vars are present', () => { + const config = createAppConfigFromEnv({ + WORDPRESS_API_URL: 'https://example.com/', + WORDPRESS_USERNAME: 'admin', + WORDPRESS_PASSWORD: 'secret', + }) + + expect(config.fallbackSite).toEqual({ + siteUrl: 'https://example.com', + username: 'admin', + password: 'secret', + }) + }) + + it('rejects partial single-site env fallback', () => { + expect(() => + createAppConfigFromEnv({ + WORDPRESS_API_URL: 'https://example.com', + WORDPRESS_USERNAME: 'admin', + }), + ).toThrow('Single-site env fallback') + }) + + it('rejects legacy multi-site env vars', () => { + expect(() => + createAppConfigFromEnv({ + WORDPRESS_1_URL: 'https://example.com', + WORDPRESS_1_USERNAME: 'admin', + WORDPRESS_1_PASSWORD: 'secret', + }), + ).toThrow('Legacy multi-site WORDPRESS_N_* environment variables') + }) +}) + +describe('resolveWordPressRequestConfig', () => { + it('prefers hidden values over env fallback', () => { + const resolved = resolveWordPressRequestConfig( + { + siteUrl: 'https://hidden.example.com/', + username: 'hidden-user', + password: 'hidden-password', + sqlEndpoint: '/custom/sql', + }, + TEST_DEFAULTS, + ) + + expect(resolved).toEqual({ + site: { + siteUrl: 'https://hidden.example.com', + username: 'hidden-user', + password: 'hidden-password', + }, + sqlEndpoint: '/custom/sql', + }) + }) + + it('uses env fallback when hidden values are absent', () => { + const resolved = resolveWordPressRequestConfig(undefined, TEST_DEFAULTS) + + expect(resolved).toEqual({ + site: TEST_DEFAULTS.fallbackSite!, + sqlEndpoint: '/mcp/v1/query', + }) + }) + + it('throws when no hidden values or env fallback are available', () => { + expect(() => + resolveWordPressRequestConfig(undefined, { + fallbackSite: undefined, + defaultSqlEndpoint: '/mcp/v1/query', + }), + ).toThrow('WordPress credentials are required') + }) +}) +