diff --git a/.github/workflows/accuracy-tests.yml b/.github/workflows/accuracy-tests.yml index 3a03c558f..0b31b7c97 100644 --- a/.github/workflows/accuracy-tests.yml +++ b/.github/workflows/accuracy-tests.yml @@ -29,7 +29,7 @@ jobs: steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json cache: "npm" diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 74e0a9210..64570da62 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json cache: "npm" @@ -31,7 +31,7 @@ jobs: steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json cache: "npm" @@ -45,7 +45,7 @@ jobs: steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json cache: "npm" diff --git a/.github/workflows/code_health.yaml b/.github/workflows/code_health.yaml index 9a0b87dd9..2dfba473d 100644 --- a/.github/workflows/code_health.yaml +++ b/.github/workflows/code_health.yaml @@ -21,7 +21,7 @@ jobs: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 if: matrix.os == 'ubuntu-latest' - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json cache: "npm" @@ -43,7 +43,7 @@ jobs: steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json cache: "npm" @@ -54,7 +54,7 @@ jobs: MDB_MCP_API_CLIENT_ID: ${{ secrets.TEST_ATLAS_CLIENT_ID }} MDB_MCP_API_CLIENT_SECRET: ${{ secrets.TEST_ATLAS_CLIENT_SECRET }} MDB_MCP_API_BASE_URL: ${{ vars.TEST_ATLAS_BASE_URL }} - run: npm test -- --exclude "tests/unit/**" --exclude "tests/integration/tools/mongodb/**" --exclude "tests/integration/*.ts" + run: npm test -- tests/integration/tools/atlas - name: Upload test results uses: actions/upload-artifact@v4 if: always() @@ -69,7 +69,7 @@ jobs: needs: [run-tests, run-atlas-tests] steps: - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json cache: "npm" diff --git a/.github/workflows/code_health_fork.yaml b/.github/workflows/code_health_fork.yaml index 3bd34cd4f..f5ef5f763 100644 --- a/.github/workflows/code_health_fork.yaml +++ b/.github/workflows/code_health_fork.yaml @@ -20,7 +20,7 @@ jobs: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 if: matrix.os == 'ubuntu-latest' - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json cache: "npm" diff --git a/.github/workflows/jira-issue.yml b/.github/workflows/jira-issue.yml index afd73d739..3760b59c4 100644 --- a/.github/workflows/jira-issue.yml +++ b/.github/workflows/jira-issue.yml @@ -62,7 +62,7 @@ jobs: - name: Remove create-jira label if: github.event.action == 'labeled' && github.event.label.name == 'create-jira' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | try { diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index 96bb5b64c..bb497ab91 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -23,7 +23,7 @@ jobs: app-id: ${{ vars.DEVTOOLS_BOT_APP_ID }} private-key: ${{ secrets.DEVTOOLS_BOT_PRIVATE_KEY }} - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json registry-url: "https://registry.npmjs.org" diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 60c478f5a..0513b2220 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -12,12 +12,13 @@ jobs: outputs: VERSION_EXISTS: ${{ steps.check-version.outputs.VERSION_EXISTS }} VERSION: ${{ steps.get-version.outputs.VERSION }} + RELEASE_CHANNEL: ${{ steps.npm-tag.outputs.RELEASE_CHANNEL }} steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v5 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json registry-url: "https://registry.npmjs.org" @@ -42,6 +43,33 @@ jobs: else echo "VERSION_EXISTS=false" >> "$GITHUB_OUTPUT" fi + - name: Get npm tag + id: npm-tag + shell: bash + run: | + set -e + VERSION="${{ steps.get-version.outputs.VERSION }}" + + # Extract the release channel (latest, alpha, beta, rc) + if [[ $VERSION =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-(.+))?$ ]]; then + if [[ -n "${BASH_REMATCH[2]}" ]]; then + CAPTURED_CHANNEL="${BASH_REMATCH[2]}" + # The captured channel might have more dots, cases like + # v1.2.3-alpha.1 For such cases we only want the channel relevant + # part which is alpha. + RELEASE_CHANNEL="${CAPTURED_CHANNEL%%.*}" + else + RELEASE_CHANNEL="latest" + fi + else + echo "::error title=Invalid Version::Encountered unexpected version ${{ steps.get-version.outputs.VERSION }}, cannot proceed!" + exit 1 + fi + + echo "RELEASE_CHANNEL=${RELEASE_CHANNEL}" >> "$GITHUB_OUTPUT" + - name: Output deployment info + run: echo "::notice title=Deployment Info::Deploying version ${{ steps.get-version.outputs.VERSION }} to channel ${{ steps.npm-tag.outputs.RELEASE_CHANNEL }}" + publish: runs-on: ubuntu-latest environment: Production @@ -53,21 +81,22 @@ jobs: steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version-file: package.json registry-url: "https://registry.npmjs.org" cache: "npm" + - name: Build package run: | npm ci npm run build - name: Publish to NPM - run: npm publish + run: npm publish --tag ${{ needs.check.outputs.RELEASE_CHANNEL }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Publish git release env: GH_TOKEN: ${{ github.token }} run: | - gh release create ${{ needs.check.outputs.VERSION }} --title "${{ needs.check.outputs.VERSION }}" --generate-notes --target ${{ github.sha }} + gh release create ${{ needs.check.outputs.VERSION }} --title "${{ needs.check.outputs.VERSION }}" --generate-notes --target ${{ github.sha }} ${{ (steps.npm-tag.outputs.RELEASE_CHANNEL != 'latest' && '--prerelease') || ''}} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index fb2ec4d68..3e98ab943 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -15,7 +15,7 @@ jobs: pull-requests: write steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - - uses: actions/stale@v9 + - uses: actions/stale@v10 id: stale with: repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.smithery/Dockerfile b/.smithery/Dockerfile index a2f4aed9c..d6db33d6c 100644 --- a/.smithery/Dockerfile +++ b/.smithery/Dockerfile @@ -2,10 +2,11 @@ # ----- Build Stage ----- FROM node:lts-alpine AS builder -RUN adduser -D mcpuser -USER mcpuser +RUN addgroup -S mcp && adduser -S mcp -G mcp -s /sbin/nologin +RUN chown -R mcp:mcp /home/mcp +USER mcp -WORKDIR /app +WORKDIR /home/mcp # Copy package and configuration COPY ../package.json ../package-lock.json ../tsconfig.json ../tsconfig.build.json ./ @@ -18,9 +19,12 @@ RUN npm ci && npm run build # ----- Production Stage ----- FROM node:lts-alpine +RUN addgroup -S mcp && adduser -S mcp -G mcp -s /sbin/nologin +RUN chown -R mcp:mcp /dist +USER mcp # Copy built artifacts -COPY --from=builder /app/dist ./dist +COPY --from=builder /home/mcp/dist ./dist # Copy package.json for production install COPY ../package.json ../package-lock.json ./ diff --git a/README.md b/README.md index d508b755d..b91c1fbc4 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,9 @@ node -v ### Quick Start -**Note:** When using Atlas API credentials, be sure to assign only the minimum required permissions to your service account. See [Atlas API Permissions](#atlas-api-permissions) for details. +> **🔒 Security Recommendation 1:** When using Atlas API credentials, be sure to assign only the minimum required permissions to your service account. See [Atlas API Permissions](#atlas-api-permissions) for details. + +> **🔒 Security Recommendation 2:** For enhanced security, we strongly recommend using environment variables to pass sensitive configuration such as connection strings and API credentials instead of command line arguments. Command line arguments can be visible in process lists and logged in various system locations, potentially exposing your secrets. Environment variables provide a more secure way to handle sensitive information. Most MCP clients require a configuration file to be created or modified to add the MCP server. @@ -60,22 +62,19 @@ Note: The configuration file syntax can be different across clients. Please refe > **Default Safety Notice:** All examples below include `--readOnly` by default to ensure safe, read-only access to your data. Remove `--readOnly` if you need to enable write operations. -#### Option 1: Connection String args +#### Option 1: Connection String -You can pass your connection string via args, make sure to use a valid username and password. +You can pass your connection string via environment variables, make sure to use a valid username and password. ```json { "mcpServers": { "MongoDB": { "command": "npx", - "args": [ - "-y", - "mongodb-mcp-server", - "--connectionString", - "mongodb://localhost:27017/myDatabase", - "--readOnly" - ] + "args": ["-y", "mongodb-mcp-server@latest", "--readOnly"], + "env": { + "MDB_MCP_CONNECTION_STRING": "mongodb://localhost:27017/myDatabase" + } } } } @@ -83,7 +82,7 @@ You can pass your connection string via args, make sure to use a valid username NOTE: The connection string can be configured to connect to any MongoDB cluster, whether it's a local instance or an Atlas cluster. -#### Option 2: Atlas API credentials args +#### Option 2: Atlas API Credentials Use your Atlas API Service Accounts credentials. Must follow all the steps in [Atlas API Access](#atlas-api-access) section. @@ -92,43 +91,37 @@ Use your Atlas API Service Accounts credentials. Must follow all the steps in [A "mcpServers": { "MongoDB": { "command": "npx", - "args": [ - "-y", - "mongodb-mcp-server", - "--apiClientId", - "your-atlas-service-accounts-client-id", - "--apiClientSecret", - "your-atlas-service-accounts-client-secret", - "--readOnly" - ] + "args": ["-y", "mongodb-mcp-server@latest", "--readOnly"], + "env": { + "MDB_MCP_API_CLIENT_ID": "your-atlas-service-accounts-client-id", + "MDB_MCP_API_CLIENT_SECRET": "your-atlas-service-accounts-client-secret" + } } } } ``` -#### Option 3: Standalone Service using command arguments +#### Option 3: Standalone Service using environment variables and command line arguments -Start Server using npx command: +You can source environment variables defined in a config file or explicitly set them like we do in the example below and run the server via npx. ```shell - npx -y mongodb-mcp-server@latest --apiClientId="your-atlas-service-accounts-client-id" --apiClientSecret="your-atlas-service-accounts-client-secret" --readOnly -``` - -- For a complete list of arguments see [Configuration Options](#configuration-options) -- To configure your Atlas Service Accounts credentials please refer to [Atlas API Access](#atlas-api-access) - -#### Option 4: Standalone Service using environment variables +# Set your credentials as environment variables first +export MDB_MCP_API_CLIENT_ID="your-atlas-service-accounts-client-id" +export MDB_MCP_API_CLIENT_SECRET="your-atlas-service-accounts-client-secret" -```shell - npx -y mongodb-mcp-server@latest --readOnly +# Then start the server +npx -y mongodb-mcp-server@latest --readOnly ``` -You can use environment variables in the config file or set them and run the server via npx. +> **💡 Platform Note:** The examples above use Unix/Linux/macOS syntax. For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions. +- For a complete list of configuration options see [Configuration Options](#configuration-options) +- To configure your Atlas Service Accounts credentials please refer to [Atlas API Access](#atlas-api-access) - Connection String via environment variables in the MCP file [example](#connection-string-with-environment-variables) - Atlas API credentials via environment variables in the MCP file [example](#atlas-api-credentials-with-environment-variables) -#### Option 5: Using Docker +#### Option 4: Using Docker You can run the MongoDB MCP Server in a Docker container, which provides isolation and doesn't require a local Node.js installation. @@ -146,22 +139,35 @@ docker run --rm -i \ ##### Option B: With MongoDB connection string ```shell +# Set your credentials as environment variables first +export MDB_MCP_CONNECTION_STRING="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" + +# Then start the docker container docker run --rm -i \ - -e MDB_MCP_CONNECTION_STRING="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" \ + -e MDB_MCP_CONNECTION_STRING \ -e MDB_MCP_READ_ONLY="true" \ mongodb/mongodb-mcp-server:latest ``` +> **💡 Platform Note:** The examples above use Unix/Linux/macOS syntax. For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions. + ##### Option C: With Atlas API credentials ```shell +# Set your credentials as environment variables first +export MDB_MCP_API_CLIENT_ID="your-atlas-service-accounts-client-id" +export MDB_MCP_API_CLIENT_SECRET="your-atlas-service-accounts-client-secret" + +# Then start the docker container docker run --rm -i \ - -e MDB_MCP_API_CLIENT_ID="your-atlas-service-accounts-client-id" \ - -e MDB_MCP_API_CLIENT_SECRET="your-atlas-service-accounts-client-secret" \ + -e MDB_MCP_API_CLIENT_ID \ + -e MDB_MCP_API_CLIENT_SECRET \ -e MDB_MCP_READ_ONLY="true" \ mongodb/mongodb-mcp-server:latest ``` +> **💡 Platform Note:** The examples above use Unix/Linux/macOS syntax. For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions. + ##### Docker in MCP Configuration File Without options: @@ -196,11 +202,14 @@ With connection string: "--rm", "-i", "-e", - "MDB_MCP_CONNECTION_STRING=mongodb+srv://username:password@cluster.mongodb.net/myDatabase", + "MDB_MCP_CONNECTION_STRING", "-e", "MDB_MCP_READ_ONLY=true", "mongodb/mongodb-mcp-server:latest" - ] + ], + "env": { + "MDB_MCP_CONNECTION_STRING": "mongodb+srv://username:password@cluster.mongodb.net/myDatabase" + } } } } @@ -220,17 +229,21 @@ With Atlas API credentials: "-e", "MDB_MCP_READ_ONLY=true", "-e", - "MDB_MCP_API_CLIENT_ID=your-atlas-service-accounts-client-id", + "MDB_MCP_API_CLIENT_ID", "-e", - "MDB_MCP_API_CLIENT_SECRET=your-atlas-service-accounts-client-secret", + "MDB_MCP_API_CLIENT_SECRET", "mongodb/mongodb-mcp-server:latest" - ] + ], + "env": { + "MDB_MCP_API_CLIENT_ID": "your-atlas-service-accounts-client-id", + "MDB_MCP_API_CLIENT_SECRET": "your-atlas-service-accounts-client-secret" + } } } } ``` -#### Option 6: Running as an HTTP Server +#### Option 5: Running as an HTTP Server > **⚠️ Security Notice:** This server now supports Streamable HTTP transport for remote connections. **HTTP transport is NOT recommended for production use without implementing proper authentication and security measures.** @@ -316,6 +329,8 @@ NOTE: atlas tools are only available when you set credentials on [configuration] ## Configuration +> **🔒 Security Best Practice:** We strongly recommend using environment variables for sensitive configuration such as API credentials (`MDB_MCP_API_CLIENT_ID`, `MDB_MCP_API_CLIENT_SECRET`) and connection strings (`MDB_MCP_CONNECTION_STRING`) instead of command-line arguments. Environment variables are not visible in process lists and provide better security for your sensitive data. + The MongoDB MCP Server can be configured using multiple methods, with the following precedence (highest to lowest): 1. Command-line arguments @@ -323,25 +338,27 @@ The MongoDB MCP Server can be configured using multiple methods, with the follow ### Configuration Options -| CLI Option | Environment Variable | Default | Description | -| ------------------------- | ------------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `apiClientId` | `MDB_MCP_API_CLIENT_ID` | | Atlas API client ID for authentication. Required for running Atlas tools. | -| `apiClientSecret` | `MDB_MCP_API_CLIENT_SECRET` | | Atlas API client secret for authentication. Required for running Atlas tools. | -| `connectionString` | `MDB_MCP_CONNECTION_STRING` | | MongoDB connection string for direct database connections. Optional, if not set, you'll need to call the `connect` tool before interacting with MongoDB data. | -| `loggers` | `MDB_MCP_LOGGERS` | disk,mcp | Comma separated values, possible values are `mcp`, `disk` and `stderr`. See [Logger Options](#logger-options) for details. | -| `logPath` | `MDB_MCP_LOG_PATH` | see note\* | Folder to store logs. | -| `disabledTools` | `MDB_MCP_DISABLED_TOOLS` | | An array of tool names, operation types, and/or categories of tools that will be disabled. | -| `readOnly` | `MDB_MCP_READ_ONLY` | false | When set to true, only allows read, connect, and metadata operation types, disabling create/update/delete operations. | -| `indexCheck` | `MDB_MCP_INDEX_CHECK` | false | When set to true, enforces that query operations must use an index, rejecting queries that perform a collection scan. | -| `telemetry` | `MDB_MCP_TELEMETRY` | enabled | When set to disabled, disables telemetry collection. | -| `transport` | `MDB_MCP_TRANSPORT` | stdio | Either 'stdio' or 'http'. | -| `httpPort` | `MDB_MCP_HTTP_PORT` | 3000 | Port number. | -| `httpHost` | `MDB_MCP_HTTP_HOST` | 127.0.0.1 | Host to bind the http server. | -| `idleTimeoutMs` | `MDB_MCP_IDLE_TIMEOUT_MS` | 600000 | Idle timeout for a client to disconnect (only applies to http transport). | -| `notificationTimeoutMs` | `MDB_MCP_NOTIFICATION_TIMEOUT_MS` | 540000 | Notification timeout for a client to be aware of diconnect (only applies to http transport). | -| `exportsPath` | `MDB_MCP_EXPORTS_PATH` | see note\* | Folder to store exported data files. | -| `exportTimeoutMs` | `MDB_MCP_EXPORT_TIMEOUT_MS` | 300000 | Time in milliseconds after which an export is considered expired and eligible for cleanup. | -| `exportCleanupIntervalMs` | `MDB_MCP_EXPORT_CLEANUP_INTERVAL_MS` | 120000 | Time in milliseconds between export cleanup cycles that remove expired export files. | +| CLI Option | Environment Variable | Default | Description | +| -------------------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `apiClientId` | `MDB_MCP_API_CLIENT_ID` | | Atlas API client ID for authentication. Required for running Atlas tools. | +| `apiClientSecret` | `MDB_MCP_API_CLIENT_SECRET` | | Atlas API client secret for authentication. Required for running Atlas tools. | +| `connectionString` | `MDB_MCP_CONNECTION_STRING` | | MongoDB connection string for direct database connections. Optional, if not set, you'll need to call the `connect` tool before interacting with MongoDB data. | +| `loggers` | `MDB_MCP_LOGGERS` | disk,mcp | Comma separated values, possible values are `mcp`, `disk` and `stderr`. See [Logger Options](#logger-options) for details. | +| `logPath` | `MDB_MCP_LOG_PATH` | see note\* | Folder to store logs. | +| `disabledTools` | `MDB_MCP_DISABLED_TOOLS` | | An array of tool names, operation types, and/or categories of tools that will be disabled. | +| `confirmationRequiredTools` | `MDB_MCP_CONFIRMATION_REQUIRED_TOOLS` | create-access-list,create-db-user,drop-database,drop-collection,delete-many | An array of tool names that require user confirmation before execution. **Requires the client to support [elicitation](https://modelcontextprotocol.io/specification/draft/client/elicitation)**. | +| `readOnly` | `MDB_MCP_READ_ONLY` | false | When set to true, only allows read, connect, and metadata operation types, disabling create/update/delete operations. | +| `indexCheck` | `MDB_MCP_INDEX_CHECK` | false | When set to true, enforces that query operations must use an index, rejecting queries that perform a collection scan. | +| `telemetry` | `MDB_MCP_TELEMETRY` | enabled | When set to disabled, disables telemetry collection. | +| `transport` | `MDB_MCP_TRANSPORT` | stdio | Either 'stdio' or 'http'. | +| `httpPort` | `MDB_MCP_HTTP_PORT` | 3000 | Port number. | +| `httpHost` | `MDB_MCP_HTTP_HOST` | 127.0.0.1 | Host to bind the http server. | +| `idleTimeoutMs` | `MDB_MCP_IDLE_TIMEOUT_MS` | 600000 | Idle timeout for a client to disconnect (only applies to http transport). | +| `notificationTimeoutMs` | `MDB_MCP_NOTIFICATION_TIMEOUT_MS` | 540000 | Notification timeout for a client to be aware of diconnect (only applies to http transport). | +| `exportsPath` | `MDB_MCP_EXPORTS_PATH` | see note\* | Folder to store exported data files. | +| `exportTimeoutMs` | `MDB_MCP_EXPORT_TIMEOUT_MS` | 300000 | Time in milliseconds after which an export is considered expired and eligible for cleanup. | +| `exportCleanupIntervalMs` | `MDB_MCP_EXPORT_CLEANUP_INTERVAL_MS` | 120000 | Time in milliseconds between export cleanup cycles that remove expired export files. | +| `atlasTemporaryDatabaseUserLifetimeMs` | `MDB_MCP_ATLAS_TEMPORARY_DATABASE_USER_LIFETIME_MS` | 14400000 | Time in milliseconds that temporary database users created when connecting to MongoDB Atlas clusters will remain active before being automatically deleted. | #### Logger Options @@ -361,6 +378,8 @@ You can combine multiple loggers, e.g. `--loggers disk stderr` or `export MDB_MC export MDB_MCP_LOGGERS="disk,stderr" ``` +> **💡 Platform Note:** For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions. + ##### Example: Set logger via command-line argument ```shell @@ -400,6 +419,14 @@ Operation types: - `metadata` - Tools that read metadata, such as list databases, list collections, collection schema, etc. - `connect` - Tools that allow you to connect or switch the connection to a MongoDB instance. If this is disabled, you will need to provide a connection string through the config when starting the server. +#### Require Confirmation + +If your client supports [elicitation](https://modelcontextprotocol.io/specification/draft/client/elicitation), you can set the MongoDB MCP server to request user confirmation before executing certain tools. + +When a tool is marked as requiring confirmation, the server will send an elicitation request to the client. The client with elicitation support will then prompt the user for confirmation and send the response back to the server. If the client does not support elicitation, the tool will execute without confirmation. + +You can set the `confirmationRequiredTools` configuration option to specify the names of tools which require confirmation. By default, the following tools have this setting enabled: `drop-database`, `drop-collection`, `delete-many`, `atlas-create-db-user`, `atlas-create-access-list`. + #### Read-Only Mode The `readOnly` configuration option allows you to restrict the MCP server to only use tools with "read", "connect", and "metadata" operation types. When enabled, all tools that have "create", "update" or "delete" operation types will not be registered with the server. @@ -411,6 +438,8 @@ You can enable read-only mode using: - **Environment variable**: `export MDB_MCP_READ_ONLY=true` - **Command-line argument**: `--readOnly` +> **💡 Platform Note:** For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions. + When read-only mode is active, you'll see a message in the server logs indicating which tools were prevented from registering due to this restriction. #### Index Check Mode @@ -424,6 +453,8 @@ You can enable index check mode using: - **Environment variable**: `export MDB_MCP_INDEX_CHECK=true` - **Command-line argument**: `--indexCheck` +> **💡 Platform Note:** For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions. + When index check mode is active, you'll see an error message if a query is rejected due to not using an index. #### Exports @@ -447,6 +478,8 @@ You can disable telemetry using: - **Command-line argument**: `--telemetry disabled` - **DO_NOT_TRACK environment variable**: `export DO_NOT_TRACK=1` +> **💡 Platform Note:** For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions. + ### Atlas API Access To use the Atlas API tools, you'll need to create a service account in MongoDB Atlas: @@ -500,7 +533,9 @@ For a full list of roles and their privileges, see the [Atlas User Roles documen Set environment variables with the prefix `MDB_MCP_` followed by the option name in uppercase with underscores: -```shell +**Linux/macOS (bash/zsh):** + +```bash # Set Atlas API credentials (via Service Accounts) export MDB_MCP_API_CLIENT_ID="your-atlas-service-accounts-client-id" export MDB_MCP_API_CLIENT_SECRET="your-atlas-service-accounts-client-secret" @@ -508,8 +543,33 @@ export MDB_MCP_API_CLIENT_SECRET="your-atlas-service-accounts-client-secret" # Set a custom MongoDB connection string export MDB_MCP_CONNECTION_STRING="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" +# Set log path export MDB_MCP_LOG_PATH="/path/to/logs" +``` + +**Windows Command Prompt (cmd):** + +```cmd +set "MDB_MCP_API_CLIENT_ID=your-atlas-service-accounts-client-id" +set "MDB_MCP_API_CLIENT_SECRET=your-atlas-service-accounts-client-secret" +set "MDB_MCP_CONNECTION_STRING=mongodb+srv://username:password@cluster.mongodb.net/myDatabase" + +set "MDB_MCP_LOG_PATH=C:\path\to\logs" +``` + +**Windows PowerShell:** + +```powershell +# Set Atlas API credentials (via Service Accounts) +$env:MDB_MCP_API_CLIENT_ID="your-atlas-service-accounts-client-id" +$env:MDB_MCP_API_CLIENT_SECRET="your-atlas-service-accounts-client-secret" + +# Set a custom MongoDB connection string +$env:MDB_MCP_CONNECTION_STRING="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" + +# Set log path +$env:MDB_MCP_LOG_PATH="C:\path\to\logs" ``` #### MCP configuration file examples @@ -551,14 +611,26 @@ export MDB_MCP_LOG_PATH="/path/to/logs" Pass configuration options as command-line arguments when starting the server: +> **🔒 Security Note:** For sensitive configuration like API credentials and connection strings, use environment variables instead of command-line arguments. + ```shell -npx -y mongodb-mcp-server@latest --apiClientId="your-atlas-service-accounts-client-id" --apiClientSecret="your-atlas-service-accounts-client-secret" --connectionString="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" --logPath=/path/to/logs --readOnly --indexCheck +# Set sensitive data as environment variable +export MDB_MCP_API_CLIENT_ID="your-atlas-service-accounts-client-id" +export MDB_MCP_API_CLIENT_SECRET="your-atlas-service-accounts-client-secret" +export MDB_MCP_CONNECTION_STRING="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" + +# Start the server with command line arguments +npx -y mongodb-mcp-server@latest --logPath=/path/to/logs --readOnly --indexCheck ``` +> **💡 Platform Note:** The examples above use Unix/Linux/macOS syntax. For Windows users, see [Environment Variables](#environment-variables) for platform-specific instructions. + #### MCP configuration file examples ##### Connection String with command-line arguments +> **🔒 Security Note:** We do not recommend passing connection string as command line argument. Connection string might contain credentials which can be visible in process lists and logged in various system locations, potentially exposing your credentials. Instead configure [connection string through environment variables](#connection-string-with-environment-variables) + ```json { "mcpServers": { @@ -578,6 +650,8 @@ npx -y mongodb-mcp-server@latest --apiClientId="your-atlas-service-accounts-clie ##### Atlas API credentials with command-line arguments +> **🔒 Security Note:** We do not recommend passing Atlas API credentials as command line argument. The provided credentials can be visible in process lists and logged in various system locations, potentially exposing your credentials. Instead configure [Atlas API credentials through environment variables](#atlas-api-credentials-with-environment-variables) + ```json { "mcpServers": { diff --git a/eslint-rules/no-config-imports.js b/eslint-rules/no-config-imports.js index 908dd5ae7..5c4efb7cb 100644 --- a/eslint-rules/no-config-imports.js +++ b/eslint-rules/no-config-imports.js @@ -10,6 +10,10 @@ const allowedConfigValueImportFiles = [ "src/index.ts", // Config resource definition that works with the some config values "src/resources/common/config.ts", + // The file exports, a factory function to create MCPConnectionManager and + // it relies on driver options generator and default driver options from + // config file. + "src/common/connectionManager.ts", ]; // Ref: https://eslint.org/docs/latest/extend/custom-rules diff --git a/package-lock.json b/package-lock.json index 8d9ac800d..b0ca67ed7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,35 +1,35 @@ { "name": "mongodb-mcp-server", - "version": "0.3.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mongodb-mcp-server", - "version": "0.3.0", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@modelcontextprotocol/sdk": "^1.15.0", + "@modelcontextprotocol/sdk": "^1.17.4", "@mongodb-js/device-id": "^0.3.1", - "@mongodb-js/devtools-connect": "^3.9.2", - "@mongodb-js/devtools-proxy-support": "^0.5.1", + "@mongodb-js/devtools-connect": "^3.9.3", + "@mongodb-js/devtools-proxy-support": "^0.5.2", "@mongosh/arg-parser": "^3.14.0", - "@mongosh/service-provider-node-driver": "^3.10.2", + "@mongosh/service-provider-node-driver": "~3.12.0", "@vitest/eslint-plugin": "^1.3.4", "bson": "^6.10.4", "express": "^5.1.0", - "kerberos": "*", "lru-cache": "^11.1.0", - "mongodb": "^6.17.0", + "mongodb": "^6.19.0", "mongodb-connection-string-url": "^3.0.2", "mongodb-log-writer": "^2.4.1", - "mongodb-redact": "^1.1.8", + "mongodb-redact": "^1.2.0", "mongodb-schema": "^12.6.2", "node-fetch": "^3.3.2", "node-machine-id": "1.1.12", - "oauth4webapi": "^3.6.0", + "oauth4webapi": "^3.8.0", "openapi-fetch": "^0.14.0", - "yargs-parser": "^22.0.0", + "ts-levenshtein": "^1.0.7", + "yargs-parser": "^21.1.1", "zod": "^3.25.76" }, "bin": { @@ -39,13 +39,13 @@ "@ai-sdk/azure": "^1.3.24", "@ai-sdk/google": "^1.2.22", "@ai-sdk/openai": "^1.3.23", - "@eslint/js": "^9.30.1", + "@eslint/js": "^9.34.0", "@modelcontextprotocol/inspector": "^0.16.5", "@mongodb-js/oidc-mock-provider": "^0.11.3", - "@redocly/cli": "^2.0.7", - "@types/express": "^5.0.1", + "@redocly/cli": "^2.0.8", + "@types/express": "^5.0.3", "@types/http-proxy": "^1.17.16", - "@types/node": "^24.0.12", + "@types/node": "^24.3.0", "@types/proper-lockfile": "^4.1.4", "@types/semver": "^7.7.0", "@types/simple-oauth2": "^5.0.7", @@ -53,24 +53,24 @@ "@vitest/coverage-v8": "^3.2.4", "ai": "^4.3.17", "duplexpair": "^1.0.2", - "eslint": "^9.30.1", - "eslint-config-prettier": "^10.1.5", - "eslint-plugin-prettier": "^5.5.1", + "eslint": "^9.34.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", "globals": "^16.3.0", "mongodb-runner": "^5.9.2", "ollama-ai-provider": "^1.2.0", "openapi-types": "^12.1.3", - "openapi-typescript": "^7.8.0", + "openapi-typescript": "^7.9.1", "prettier": "^3.6.2", "proper-lockfile": "^4.1.2", "semver": "^7.7.2", "simple-git": "^3.28.0", - "tsx": "^4.20.3", - "typescript": "^5.8.3", - "typescript-eslint": "^8.36.0", + "tsx": "^4.20.5", + "typescript": "^5.9.2", + "typescript-eslint": "^8.41.0", "uuid": "^11.1.0", "vitest": "^3.2.4", - "yaml": "^2.8.0" + "yaml": "^2.8.1" }, "engines": { "node": "^20.19.0 || ^22.12.0 || >= 23.0.0" @@ -79,17 +79,14 @@ "kerberos": "^2.2.2" } }, - "@himanshusinghs/ai-sdk-google": { - "extraneous": true - }, "node_modules/@ai-sdk/azure": { - "version": "1.3.24", - "resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-1.3.24.tgz", - "integrity": "sha512-6zOG8mwmd8esSL/L9oYFZSyZWORRTxuG6on9A3RdPe7MRJ607Q6BWsuvul79kecbLf5xQ4bfP7LzXaBizsd8OA==", + "version": "1.3.25", + "resolved": "https://registry.npmjs.org/@ai-sdk/azure/-/azure-1.3.25.tgz", + "integrity": "sha512-cTME89A9UYrza0t5pbY9b80yYY02Q5ALQdB2WP3R7/Yl1PLwbFChx994Q3Un0G2XV5h3arlm4fZTViY10isjhQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@ai-sdk/openai": "1.3.23", + "@ai-sdk/openai": "1.3.24", "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, @@ -118,9 +115,9 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "1.3.23", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-1.3.23.tgz", - "integrity": "sha512-86U7rFp8yacUAOE/Jz8WbGcwMCqWvjK33wk5DXkfnAOEn3mx2r7tNSJdjukQFZbAK97VMXGPPHxF+aEARDXRXQ==", + "version": "1.3.24", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-1.3.24.tgz", + "integrity": "sha512-GYXnGJTHRTZc4gJMSmFRgEQudjqd4PUN0ZjQhPwOAYH1yOAvQoG/Ikqs+HyISRbLPCrhbZnPKCNHuRU4OfpW0Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -348,48 +345,48 @@ } }, "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.844.0.tgz", - "integrity": "sha512-LwuYN43+IWQ5hOSaaNx6VVrUbLZibaZ01pXNuwdbaJGZOKcCCnev5O7MY0Kud7xatJrf7B9l2GIZW7gmHFi+yQ==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.879.0.tgz", + "integrity": "sha512-uMvvNmRs5shbbS2R3ZiouILpoyHUl4t2hPzp8rzqsdmvpr43SGy+L7ZKz1VxPK71xT6ZOZPU4+qEI657H3j3Yw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.844.0", - "@aws-sdk/credential-provider-node": "3.844.0", - "@aws-sdk/middleware-host-header": "3.840.0", - "@aws-sdk/middleware-logger": "3.840.0", - "@aws-sdk/middleware-recursion-detection": "3.840.0", - "@aws-sdk/middleware-user-agent": "3.844.0", - "@aws-sdk/region-config-resolver": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.844.0", - "@aws-sdk/util-user-agent-browser": "3.840.0", - "@aws-sdk/util-user-agent-node": "3.844.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.7.0", - "@smithy/fetch-http-handler": "^5.1.0", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.14", - "@smithy/middleware-retry": "^4.1.15", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.1.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.6", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/credential-provider-node": "3.879.0", + "@aws-sdk/middleware-host-header": "3.873.0", + "@aws-sdk/middleware-logger": "3.876.0", + "@aws-sdk/middleware-recursion-detection": "3.873.0", + "@aws-sdk/middleware-user-agent": "3.879.0", + "@aws-sdk/region-config-resolver": "3.873.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-endpoints": "3.879.0", + "@aws-sdk/util-user-agent-browser": "3.873.0", + "@aws-sdk/util-user-agent-node": "3.879.0", + "@smithy/config-resolver": "^4.1.5", + "@smithy/core": "^3.9.0", + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/hash-node": "^4.0.5", + "@smithy/invalid-dependency": "^4.0.5", + "@smithy/middleware-content-length": "^4.0.5", + "@smithy/middleware-endpoint": "^4.1.19", + "@smithy/middleware-retry": "^4.1.20", + "@smithy/middleware-serde": "^4.0.9", + "@smithy/middleware-stack": "^4.0.5", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.22", - "@smithy/util-defaults-mode-node": "^4.0.22", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", + "@smithy/util-defaults-mode-browser": "^4.0.27", + "@smithy/util-defaults-mode-node": "^4.0.27", + "@smithy/util-endpoints": "^3.0.7", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-retry": "^4.0.7", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -398,47 +395,47 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.844.0.tgz", - "integrity": "sha512-FktodSx+pfUfIqMjoNwZ6t1xqq/G3cfT7I4JJ0HKHoIIZdoCHQB52x0OzKDtHDJAnEQPInasdPS8PorZBZtHmg==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.879.0.tgz", + "integrity": "sha512-+Pc3OYFpRYpKLKRreovPM63FPPud1/SF9vemwIJfz6KwsBCJdvg7vYD1xLSIp5DVZLeetgf4reCyAA5ImBfZuw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.844.0", - "@aws-sdk/middleware-host-header": "3.840.0", - "@aws-sdk/middleware-logger": "3.840.0", - "@aws-sdk/middleware-recursion-detection": "3.840.0", - "@aws-sdk/middleware-user-agent": "3.844.0", - "@aws-sdk/region-config-resolver": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.844.0", - "@aws-sdk/util-user-agent-browser": "3.840.0", - "@aws-sdk/util-user-agent-node": "3.844.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.7.0", - "@smithy/fetch-http-handler": "^5.1.0", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.14", - "@smithy/middleware-retry": "^4.1.15", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.1.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.6", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/middleware-host-header": "3.873.0", + "@aws-sdk/middleware-logger": "3.876.0", + "@aws-sdk/middleware-recursion-detection": "3.873.0", + "@aws-sdk/middleware-user-agent": "3.879.0", + "@aws-sdk/region-config-resolver": "3.873.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-endpoints": "3.879.0", + "@aws-sdk/util-user-agent-browser": "3.873.0", + "@aws-sdk/util-user-agent-node": "3.879.0", + "@smithy/config-resolver": "^4.1.5", + "@smithy/core": "^3.9.0", + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/hash-node": "^4.0.5", + "@smithy/invalid-dependency": "^4.0.5", + "@smithy/middleware-content-length": "^4.0.5", + "@smithy/middleware-endpoint": "^4.1.19", + "@smithy/middleware-retry": "^4.1.20", + "@smithy/middleware-serde": "^4.0.9", + "@smithy/middleware-stack": "^4.0.5", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.22", - "@smithy/util-defaults-mode-node": "^4.0.22", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", + "@smithy/util-defaults-mode-browser": "^4.0.27", + "@smithy/util-defaults-mode-node": "^4.0.27", + "@smithy/util-endpoints": "^3.0.7", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-retry": "^4.0.7", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -447,23 +444,23 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.844.0.tgz", - "integrity": "sha512-pfpI54bG5Xf2NkqrDBC2REStXlDXNCw/whORhkEs+Tp5exU872D5QKguzjPA6hH+8Pvbq1qgt5zXMbduISTHJw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.840.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.7.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.6", - "@smithy/types": "^4.3.1", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.879.0.tgz", + "integrity": "sha512-AhNmLCrx980LsK+SfPXGh7YqTyZxsK0Qmy18mWmkfY0TSq7WLaSDB5zdQbgbnQCACCHy8DUYXbi4KsjlIhv3PA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@aws-sdk/xml-builder": "3.873.0", + "@smithy/core": "^3.9.0", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/property-provider": "^4.0.5", + "@smithy/protocol-http": "^5.1.3", + "@smithy/signature-v4": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", + "@smithy/util-middleware": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" @@ -472,16 +469,46 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/core/node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/core/node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.844.0.tgz", - "integrity": "sha512-LBigff8jHYZbQTRcybiqamZTQpRb63CBiCG9Ce0C1CzmZQ0WUZFmJA5ZbqwUK+BliOEdpl6kQFgsf6sz9ODbZg==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.879.0.tgz", + "integrity": "sha512-E1iQ4+eyDKJfWVuijIxxNZ+uhZ3LF3HXnYbkguq05jIbbazXmN/AXTfQoXreXYoGzOSJltxkje9X0H7rBJRxtg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-cognito-identity": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/client-cognito-identity": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -489,15 +516,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.844.0.tgz", - "integrity": "sha512-WB94Ox86MqcZ4CnRjKgopzaSuZH4hMP0GqdOxG4s1it1lRWOIPOHOC1dPiM0Zbj1uqITIhbXUQVXyP/uaJeNkw==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.879.0.tgz", + "integrity": "sha512-JgG7A8SSbr5IiCYL8kk39Y9chdSB5GPwBorDW8V8mr19G9L+qd6ohED4fAocoNFaDnYJ5wGAHhCfSJjzcsPBVQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -505,20 +532,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.844.0.tgz", - "integrity": "sha512-e+efVqfkhpM8zxYeiLNgTUlX+tmtXzVm3bw1A02U9Z9cWBHyQNb8pi90M7QniLoqRURY1B0C2JqkOE61gd4KNg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/fetch-http-handler": "^5.1.0", - "@smithy/node-http-handler": "^4.1.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.6", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.3", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.879.0.tgz", + "integrity": "sha512-2hM5ByLpyK+qORUexjtYyDZsgxVCCUiJQZRMGkNXFEGz6zTpbjfTIWoh3zRgWHEBiqyPIyfEy50eIF69WshcuA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/property-provider": "^4.0.5", + "@smithy/protocol-http": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/util-stream": "^4.2.4", "tslib": "^2.6.2" }, "engines": { @@ -526,23 +553,23 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.844.0.tgz", - "integrity": "sha512-jc5ArGz2HfAx5QPXD+Ep36+QWyCKzl2TG6Vtl87/vljfLhVD0gEHv8fRsqWEp3Rc6hVfKnCjLW5ayR2HYcow9w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.844.0", - "@aws-sdk/credential-provider-env": "3.844.0", - "@aws-sdk/credential-provider-http": "3.844.0", - "@aws-sdk/credential-provider-process": "3.844.0", - "@aws-sdk/credential-provider-sso": "3.844.0", - "@aws-sdk/credential-provider-web-identity": "3.844.0", - "@aws-sdk/nested-clients": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.879.0.tgz", + "integrity": "sha512-07M8zfb73KmMBqVO5/V3Ea9kqDspMX0fO0kaI1bsjWI6ngnMye8jCE0/sIhmkVAI0aU709VA0g+Bzlopnw9EoQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/credential-provider-env": "3.879.0", + "@aws-sdk/credential-provider-http": "3.879.0", + "@aws-sdk/credential-provider-process": "3.879.0", + "@aws-sdk/credential-provider-sso": "3.879.0", + "@aws-sdk/credential-provider-web-identity": "3.879.0", + "@aws-sdk/nested-clients": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/credential-provider-imds": "^4.0.7", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -550,22 +577,22 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.844.0.tgz", - "integrity": "sha512-pUqB0StTNyW0R03XjTA3wrQZcie/7FJKSXlYHue921ZXuhLOZpzyDkLNfdRsZTcEoYYWVPSmyS+Eu/g5yVsBNA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.844.0", - "@aws-sdk/credential-provider-http": "3.844.0", - "@aws-sdk/credential-provider-ini": "3.844.0", - "@aws-sdk/credential-provider-process": "3.844.0", - "@aws-sdk/credential-provider-sso": "3.844.0", - "@aws-sdk/credential-provider-web-identity": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.879.0.tgz", + "integrity": "sha512-FYaAqJbnSTrVL2iZkNDj2hj5087yMv2RN2GA8DJhe7iOJjzhzRojrtlfpWeJg6IhK0sBKDH+YXbdeexCzUJvtA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.879.0", + "@aws-sdk/credential-provider-http": "3.879.0", + "@aws-sdk/credential-provider-ini": "3.879.0", + "@aws-sdk/credential-provider-process": "3.879.0", + "@aws-sdk/credential-provider-sso": "3.879.0", + "@aws-sdk/credential-provider-web-identity": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/credential-provider-imds": "^4.0.7", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -573,16 +600,16 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.844.0.tgz", - "integrity": "sha512-VCI8XvIDt2WBfk5Gi/wXKPcWTS3OkAbovB66oKcNQalllH8ESDg4SfLNhchdnN8A5sDGj6tIBJ19nk+dQ6GaqQ==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.879.0.tgz", + "integrity": "sha512-7r360x1VyEt35Sm1JFOzww2WpnfJNBbvvnzoyLt7WRfK0S/AfsuWhu5ltJ80QvJ0R3AiSNbG+q/btG2IHhDYPQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -590,18 +617,18 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.844.0.tgz", - "integrity": "sha512-UNp/uWufGlb5nWa4dpc6uQnDOB/9ysJJFG95ACowNVL9XWfi1LJO7teKrqNkVhq0CzSJS1tCt3FvX4UfM+aN1g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.844.0", - "@aws-sdk/core": "3.844.0", - "@aws-sdk/token-providers": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.879.0.tgz", + "integrity": "sha512-gd27B0NsgtKlaPNARj4IX7F7US5NuU691rGm0EUSkDsM7TctvJULighKoHzPxDQlrDbVI11PW4WtKS/Zg5zPlQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.879.0", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/token-providers": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -609,16 +636,16 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.844.0.tgz", - "integrity": "sha512-iDmX4pPmatjttIScdspZRagaFnCjpHZIEEwTyKdXxUaU0iAOSXF8ecrCEvutETvImPOC86xdrq+MPacJOnMzUA==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.879.0.tgz", + "integrity": "sha512-Jy4uPFfGzHk1Mxy+/Wr43vuw9yXsE2yiF4e4598vc3aJfO0YtA2nSfbKD3PNKRORwXbeKqWPfph9SCKQpWoxEg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.844.0", - "@aws-sdk/nested-clients": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/nested-clients": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -626,29 +653,29 @@ } }, "node_modules/@aws-sdk/credential-providers": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.844.0.tgz", - "integrity": "sha512-amTf3wxwTVNV5jBpN1dT77c5rlch3ooUhBxA+dAnlKLLbc0OlcUrF49Kh69PWBlACahcZDuBh/KPJm2wiIMyYQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.844.0", - "@aws-sdk/core": "3.844.0", - "@aws-sdk/credential-provider-cognito-identity": "3.844.0", - "@aws-sdk/credential-provider-env": "3.844.0", - "@aws-sdk/credential-provider-http": "3.844.0", - "@aws-sdk/credential-provider-ini": "3.844.0", - "@aws-sdk/credential-provider-node": "3.844.0", - "@aws-sdk/credential-provider-process": "3.844.0", - "@aws-sdk/credential-provider-sso": "3.844.0", - "@aws-sdk/credential-provider-web-identity": "3.844.0", - "@aws-sdk/nested-clients": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.7.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", + "version": "3.880.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.880.0.tgz", + "integrity": "sha512-QJsAyjXFn/v0uvcVkT8hbIH8WeAUAQkuPLasOJkyi3TiTH8AxPWxY+YLeIKoyiVcTRunRca+29AoLX1F0TgMlg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.879.0", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/credential-provider-cognito-identity": "3.879.0", + "@aws-sdk/credential-provider-env": "3.879.0", + "@aws-sdk/credential-provider-http": "3.879.0", + "@aws-sdk/credential-provider-ini": "3.879.0", + "@aws-sdk/credential-provider-node": "3.879.0", + "@aws-sdk/credential-provider-process": "3.879.0", + "@aws-sdk/credential-provider-sso": "3.879.0", + "@aws-sdk/credential-provider-web-identity": "3.879.0", + "@aws-sdk/nested-clients": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/config-resolver": "^4.1.5", + "@smithy/core": "^3.9.0", + "@smithy/credential-provider-imds": "^4.0.7", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -656,14 +683,14 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.840.0.tgz", - "integrity": "sha512-ub+hXJAbAje94+Ya6c6eL7sYujoE8D4Bumu1NUI8TXjUhVVn0HzVWQjpRLshdLsUp1AW7XyeJaxyajRaJQ8+Xg==", + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.873.0.tgz", + "integrity": "sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.862.0", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -671,13 +698,13 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.840.0.tgz", - "integrity": "sha512-lSV8FvjpdllpGaRspywss4CtXV8M7NNNH+2/j86vMH+YCOZ6fu2T/TyFd/tHwZ92vDfHctWkRbQxg0bagqwovA==", + "version": "3.876.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.876.0.tgz", + "integrity": "sha512-cpWJhOuMSyz9oV25Z/CMHCBTgafDCbv7fHR80nlRrPdPZ8ETNsahwRgltXP1QJJ8r3X/c1kwpOR7tc+RabVzNA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.862.0", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -685,14 +712,14 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.840.0.tgz", - "integrity": "sha512-Gu7lGDyfddyhIkj1Z1JtrY5NHb5+x/CRiB87GjaSrKxkDaydtX2CU977JIABtt69l9wLbcGDIQ+W0uJ5xPof7g==", + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.873.0.tgz", + "integrity": "sha512-OtgY8EXOzRdEWR//WfPkA/fXl0+WwE8hq0y9iw2caNyKPtca85dzrrZWnPqyBK/cpImosrpR1iKMYr41XshsCg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.862.0", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -700,17 +727,17 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.844.0.tgz", - "integrity": "sha512-SIbDNUL6ZYXPj5Tk0qEz05sW9kNS1Gl3/wNWEmH+AuUACipkyIeKKWzD6z5433MllETh73vtka/JQF3g7AuZww==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.879.0.tgz", + "integrity": "sha512-DDSV8228lQxeMAFKnigkd0fHzzn5aauZMYC3CSj6e5/qE7+9OwpkUcjHfb7HZ9KWG6L2/70aKZXHqiJ4xKhOZw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.844.0", - "@smithy/core": "^3.7.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-endpoints": "3.879.0", + "@smithy/core": "^3.9.0", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -718,47 +745,47 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.844.0.tgz", - "integrity": "sha512-p2XILWc7AcevUSpBg2VtQrk79eWQC4q2JsCSY7HxKpFLZB4mMOfmiTyYkR1gEA6AttK/wpCOtfz+hi1/+z2V1A==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.879.0.tgz", + "integrity": "sha512-7+n9NpIz9QtKYnxmw1fHi9C8o0GrX8LbBR4D50c7bH6Iq5+XdSuL5AFOWWQ5cMD0JhqYYJhK/fJsVau3nUtC4g==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.844.0", - "@aws-sdk/middleware-host-header": "3.840.0", - "@aws-sdk/middleware-logger": "3.840.0", - "@aws-sdk/middleware-recursion-detection": "3.840.0", - "@aws-sdk/middleware-user-agent": "3.844.0", - "@aws-sdk/region-config-resolver": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.844.0", - "@aws-sdk/util-user-agent-browser": "3.840.0", - "@aws-sdk/util-user-agent-node": "3.844.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.7.0", - "@smithy/fetch-http-handler": "^5.1.0", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.14", - "@smithy/middleware-retry": "^4.1.15", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.1.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.6", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/middleware-host-header": "3.873.0", + "@aws-sdk/middleware-logger": "3.876.0", + "@aws-sdk/middleware-recursion-detection": "3.873.0", + "@aws-sdk/middleware-user-agent": "3.879.0", + "@aws-sdk/region-config-resolver": "3.873.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-endpoints": "3.879.0", + "@aws-sdk/util-user-agent-browser": "3.873.0", + "@aws-sdk/util-user-agent-node": "3.879.0", + "@smithy/config-resolver": "^4.1.5", + "@smithy/core": "^3.9.0", + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/hash-node": "^4.0.5", + "@smithy/invalid-dependency": "^4.0.5", + "@smithy/middleware-content-length": "^4.0.5", + "@smithy/middleware-endpoint": "^4.1.19", + "@smithy/middleware-retry": "^4.1.20", + "@smithy/middleware-serde": "^4.0.9", + "@smithy/middleware-stack": "^4.0.5", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.22", - "@smithy/util-defaults-mode-node": "^4.0.22", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", + "@smithy/util-defaults-mode-browser": "^4.0.27", + "@smithy/util-defaults-mode-node": "^4.0.27", + "@smithy/util-endpoints": "^3.0.7", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-retry": "^4.0.7", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -767,16 +794,16 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.840.0.tgz", - "integrity": "sha512-Qjnxd/yDv9KpIMWr90ZDPtRj0v75AqGC92Lm9+oHXZ8p1MjG5JE2CW0HL8JRgK9iKzgKBL7pPQRXI8FkvEVfrA==", + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.873.0.tgz", + "integrity": "sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.862.0", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/types": "^4.3.2", "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", + "@smithy/util-middleware": "^4.0.5", "tslib": "^2.6.2" }, "engines": { @@ -784,17 +811,17 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.844.0.tgz", - "integrity": "sha512-Kh728FEny0fil+LeH8U1offPJCTd/EDh8liBAvLtViLHt2WoX2xC8rk98D38Q5p79aIUhHb3Pf4n9IZfTu/Kog==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.879.0.tgz", + "integrity": "sha512-47J7sCwXdnw9plRZNAGVkNEOlSiLb/kR2slnDIHRK9NB/ECKsoqgz5OZQJ9E2f0yqOs8zSNJjn3T01KxpgW8Qw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.844.0", - "@aws-sdk/nested-clients": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/nested-clients": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -802,12 +829,12 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz", - "integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==", + "version": "3.862.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.862.0.tgz", + "integrity": "sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -815,15 +842,15 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.844.0.tgz", - "integrity": "sha512-1DHh0WTUmxlysz3EereHKtKoxVUG9UC5BsfAw6Bm4/6qDlJiqtY3oa2vebkYN23yltKdfsCK65cwnBRU59mWVg==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.879.0.tgz", + "integrity": "sha512-aVAJwGecYoEmbEFju3127TyJDF9qJsKDUUTRMDuS8tGn+QiWQFnfInmbt+el9GU1gEJupNTXV+E3e74y51fb7A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-endpoints": "^3.0.6", + "@aws-sdk/types": "3.862.0", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", + "@smithy/util-endpoints": "^3.0.7", "tslib": "^2.6.2" }, "engines": { @@ -831,9 +858,9 @@ } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", - "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.873.0.tgz", + "integrity": "sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -843,27 +870,27 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.840.0.tgz", - "integrity": "sha512-JdyZM3EhhL4PqwFpttZu1afDpPJCCc3eyZOLi+srpX11LsGj6sThf47TYQN75HT1CarZ7cCdQHGzP2uy3/xHfQ==", + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.873.0.tgz", + "integrity": "sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/types": "^4.3.1", + "@aws-sdk/types": "3.862.0", + "@smithy/types": "^4.3.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.844.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.844.0.tgz", - "integrity": "sha512-0eTpURp9Gxbyyeqr78ogARZMSWS5KUMZuN+XMHxNpQLmn2S+J3g+MAyoklCcwhKXlbdQq2aMULEiy0mqIWytuw==", + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.879.0.tgz", + "integrity": "sha512-A5KGc1S+CJRzYnuxJQQmH1BtGsz46AgyHkqReKfGiNQA8ET/9y9LQ5t2ABqnSBHHIh3+MiCcQSkUZ0S3rTodrQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.844.0", - "@aws-sdk/types": "3.840.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", + "@aws-sdk/middleware-user-agent": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -879,12 +906,12 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", - "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.873.0.tgz", + "integrity": "sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -934,13 +961,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -950,9 +977,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "dev": true, "license": "MIT", "engines": { @@ -960,9 +987,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1032,9 +1059,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], @@ -1049,9 +1076,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], @@ -1066,9 +1093,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], @@ -1083,9 +1110,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], @@ -1100,9 +1127,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -1117,9 +1144,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -1134,9 +1161,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], @@ -1151,9 +1178,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], @@ -1168,9 +1195,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], @@ -1185,9 +1212,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], @@ -1202,9 +1229,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], @@ -1219,9 +1246,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], @@ -1236,9 +1263,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], @@ -1253,9 +1280,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], @@ -1270,9 +1297,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], @@ -1287,9 +1314,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], @@ -1304,9 +1331,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], @@ -1321,9 +1348,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], @@ -1338,9 +1365,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -1355,9 +1382,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], @@ -1372,9 +1399,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -1389,9 +1416,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", "cpu": [ "arm64" ], @@ -1406,9 +1433,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -1423,9 +1450,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -1440,9 +1467,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -1457,9 +1484,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -1474,9 +1501,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz", + "integrity": "sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==", "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -1491,18 +1518,6 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", @@ -1526,16 +1541,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1549,18 +1554,18 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -1592,16 +1597,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -1627,9 +1622,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1648,12 +1643,12 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -1730,31 +1725,18 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1832,91 +1814,6 @@ "node": ">=12" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/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/@isaacs/cliui/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/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/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/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1941,9 +1838,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { @@ -1962,16 +1859,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "devOptional": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2023,9 +1920,9 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/inspector": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector/-/inspector-0.16.5.tgz", - "integrity": "sha512-3viCtcLn1p6ZYZG8L9DxnNx2XUw6damCZM37nBByKAd74stH7nZa2VxFAwLpUCjKylYqawCRthzYSyeSlWGf1g==", + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector/-/inspector-0.16.6.tgz", + "integrity": "sha512-6x6dzTf8MV6z/XIdzr/4EMK4elMn1XUzTJHxczsBePLg1G5VNAM/4g5abNFIB9bzuxJ/1VH8016Vv6S7sj/24Q==", "dev": true, "license": "MIT", "workspaces": [ @@ -2034,10 +1931,10 @@ "cli" ], "dependencies": { - "@modelcontextprotocol/inspector-cli": "^0.16.5", - "@modelcontextprotocol/inspector-client": "^0.16.5", - "@modelcontextprotocol/inspector-server": "^0.16.5", - "@modelcontextprotocol/sdk": "^1.17.3", + "@modelcontextprotocol/inspector-cli": "^0.16.6", + "@modelcontextprotocol/inspector-client": "^0.16.6", + "@modelcontextprotocol/inspector-server": "^0.16.6", + "@modelcontextprotocol/sdk": "^1.17.5", "concurrently": "^9.2.0", "open": "^10.2.0", "shell-quote": "^1.8.3", @@ -2053,13 +1950,13 @@ } }, "node_modules/@modelcontextprotocol/inspector-cli": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-cli/-/inspector-cli-0.16.5.tgz", - "integrity": "sha512-6Flp9goLJutjUZTx6clDo4x/6TA7BwqeTGSYcC8ZeP8IEKjx+EZpvpYPVAV++rkHJYa0F5PViy02CMoGBGkzkg==", + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-cli/-/inspector-cli-0.16.6.tgz", + "integrity": "sha512-28RAaGoN9XgKYvl8kOo9wTHBrLp5Th+biTt5mNGUzowMdcoG/FpI8mHROIhcgDyp+kj0SYR5fmwcb6GIxBnjUw==", "dev": true, "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.17.3", + "@modelcontextprotocol/sdk": "^1.17.5", "commander": "^13.1.0", "spawn-rx": "^5.1.2" }, @@ -2068,13 +1965,13 @@ } }, "node_modules/@modelcontextprotocol/inspector-client": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-client/-/inspector-client-0.16.5.tgz", - "integrity": "sha512-KjgtTRdFSDt964a9KtmF3aRpi4ntd+6wn3e4WUa5vcK7H8f34rq4wfIGF22dd/xoKbALP3zVVAsPqVN94SCGmg==", + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-client/-/inspector-client-0.16.6.tgz", + "integrity": "sha512-2dwB0OXI02PTTsECCTIsB9DkERImIrsTAuZW6LlfUojtQMLI5NpuUID4Y4LaYPcdGnxkkkR1eddrPTsuzgabvg==", "dev": true, "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.17.3", + "@modelcontextprotocol/sdk": "^1.17.5", "@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-dialog": "^1.1.3", "@radix-ui/react-icons": "^1.3.0", @@ -2097,7 +1994,6 @@ "react-simple-code-editor": "^0.14.1", "serve-handler": "^6.1.6", "tailwind-merge": "^2.5.3", - "tailwindcss-animate": "^1.0.7", "zod": "^3.25.76" }, "bin": { @@ -2105,13 +2001,13 @@ } }, "node_modules/@modelcontextprotocol/inspector-server": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-server/-/inspector-server-0.16.5.tgz", - "integrity": "sha512-mWKrEpimfNdSFOxMJGj3cYN1PxHANW9vjpek+tR0TLiP7ogq7DLV4bbsa+lPPGwOVogUIyUiXbffY8M1YHRZVA==", + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-server/-/inspector-server-0.16.6.tgz", + "integrity": "sha512-BkE/4K2Y8ZcXK/cGBucG+rLTcTIUAaSyQabxqh0p+ErhkJDmepDvI+63OqQnauWUJydXPZYtBQyHppL4JN7RGw==", "dev": true, "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.17.3", + "@modelcontextprotocol/sdk": "^1.17.5", "cors": "^2.8.5", "express": "^5.1.0", "ws": "^8.18.0", @@ -2122,9 +2018,9 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.4.tgz", - "integrity": "sha512-zq24hfuAmmlNZvik0FLI58uE5sriN0WWsQzIlYnzSuKDAHFqJtBFrl/LfB1NLgJT5Y7dEBzaX4yAKqOPrcetaw==", + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.5.tgz", + "integrity": "sha512-QakrKIGniGuRVfWBdMsDea/dx1PNE739QJ7gCM41s9q+qaCYTHCdsIBXQVVXry3mfWAiaM9kT22Hyz53Uw8mfg==", "license": "MIT", "dependencies": { "ajv": "^6.12.6", @@ -2160,12 +2056,12 @@ "license": "Apache-2.0" }, "node_modules/@mongodb-js/devtools-connect": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-connect/-/devtools-connect-3.9.2.tgz", - "integrity": "sha512-nuWKXYTUhe/jZ561Nn4LQHlpo6GjanUZLDtklWTaGE1dboUspJRYHAeHF+wVVvccdjpAx47Vll9mDcameG8k7w==", + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-connect/-/devtools-connect-3.9.4.tgz", + "integrity": "sha512-L/DyeoVUejkFqP9HOxJ9PgClkNL+z1We1eAzAvdseRtm0T4B7UJvBg2Fn4D84cC9mbQVuxkSRThTQnQkKW0jOA==", "license": "Apache-2.0", "dependencies": { - "@mongodb-js/devtools-proxy-support": "^0.5.1", + "@mongodb-js/devtools-proxy-support": "^0.5.3", "@mongodb-js/oidc-http-server-pages": "1.1.6", "lodash.merge": "^4.6.2", "mongodb-connection-string-url": "^3.0.0", @@ -2184,9 +2080,9 @@ } }, "node_modules/@mongodb-js/devtools-proxy-support": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-proxy-support/-/devtools-proxy-support-0.5.1.tgz", - "integrity": "sha512-snIekrl3yj6fPnk6UfTIrBj8Wt43hvjqf7XhGaw1Qcn55BOClE7FgXcJjLXOIDsEMuzdGtLnJji+GbW2uD2ulg==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-proxy-support/-/devtools-proxy-support-0.5.3.tgz", + "integrity": "sha512-m5LzS86xh7iOuHA88ibbJvBkPZ6Qm/0B4N90s7epNEOvtMo0Jr8dYNxnLYobahFkvzbHp+oPRrCsztAKs0TZYQ==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/socksv5": "^0.0.10", @@ -2281,54 +2177,10 @@ "oidc-mock-provider": "bin/oidc-mock-provider.js" } }, - "node_modules/@mongodb-js/oidc-mock-provider/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@mongodb-js/oidc-mock-provider/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@mongodb-js/oidc-mock-provider/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/@mongodb-js/oidc-plugin": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-plugin/-/oidc-plugin-2.0.2.tgz", - "integrity": "sha512-E+xStW+3qtA8Da9h/cBUDGBd0RmbOwyNEncEbhAf2ZJpTEwHxgAhVO/STmxiaRqw0u4w8EmXrGqDdyGagRhx+A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-plugin/-/oidc-plugin-2.0.3.tgz", + "integrity": "sha512-WlJYyM+p7tX8XkN2L9FbstsOOdIxwAYcwNgHx/XYVgy1gqo5mf4zjyWeohqB368WDStWb0CXUV2Bx1DM4EC/tQ==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -2363,13 +2215,13 @@ } }, "node_modules/@mongosh/arg-parser": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@mongosh/arg-parser/-/arg-parser-3.14.0.tgz", - "integrity": "sha512-ue7FtuO9rgmjrh2lFZdgtrVMrGXeqBG8mWDDTb/CZ2GZNtULhAdk9d+gdnjupQNH66fGWoKdLhuur466HuJrtw==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@mongosh/arg-parser/-/arg-parser-3.16.0.tgz", + "integrity": "sha512-b/ov/qPiPODgpdFD1pCd2GqLI1ZI9fTj8FUcceBeG1eZuajAibI7qQBbfVR2/ZfL3ygYME4l1Lork7LpQmV8Xw==", "license": "Apache-2.0", "dependencies": { - "@mongosh/errors": "2.4.2", - "@mongosh/i18n": "^2.15.2", + "@mongosh/errors": "2.4.4", + "@mongosh/i18n": "^2.15.4", "mongodb-connection-string-url": "^3.0.1" }, "engines": { @@ -2377,21 +2229,21 @@ } }, "node_modules/@mongosh/errors": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@mongosh/errors/-/errors-2.4.2.tgz", - "integrity": "sha512-p+LOHVj/VIt6cpJY1AvDmG/QLP7WZQ7q+32paU6qxjXaoC0kMqmemaqK5cUj8JWod1VEv9/Ol4T6OfWTwPG20A==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@mongosh/errors/-/errors-2.4.4.tgz", + "integrity": "sha512-Z1z8VuYYgVjleo2N/GssECbc9ZXrKcLS83zMtflGoYujQ2B7CAMB0D9YnQZAvvWd68YQD4IU5HqJkmcrtWo0Dw==", "license": "Apache-2.0", "engines": { "node": ">=14.15.1" } }, "node_modules/@mongosh/i18n": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@mongosh/i18n/-/i18n-2.15.2.tgz", - "integrity": "sha512-E286cGq9/Cgg1TjkOvuKG5ymmIZI/gUpXatK83Ulp9EbQ+tqSwDsv+A3Z+unFyRHYvFuTLVlBCXFyHRDBn8Nww==", + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/@mongosh/i18n/-/i18n-2.15.4.tgz", + "integrity": "sha512-whgXLXh4uLcmoSsZdkKfRL9tHB1y9+GC5hkdfboCi3ai18zavV2Dru+FaeLZOP+T++nL49T7OIDi2JL0pa+CTA==", "license": "Apache-2.0", "dependencies": { - "@mongosh/errors": "2.4.2" + "@mongosh/errors": "2.4.4" }, "engines": { "node": ">=14.15.1" @@ -2417,6 +2269,15 @@ "mongodb-client-encryption": "^6.3.0" } }, + "node_modules/@mongosh/service-provider-core/node_modules/@mongosh/errors": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@mongosh/errors/-/errors-2.4.2.tgz", + "integrity": "sha512-p+LOHVj/VIt6cpJY1AvDmG/QLP7WZQ7q+32paU6qxjXaoC0kMqmemaqK5cUj8JWod1VEv9/Ol4T6OfWTwPG20A==", + "license": "Apache-2.0", + "engines": { + "node": ">=14.15.1" + } + }, "node_modules/@mongosh/service-provider-node-driver": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/@mongosh/service-provider-node-driver/-/service-provider-node-driver-3.12.0.tgz", @@ -2455,6 +2316,15 @@ "node": ">= 16.20.1" } }, + "node_modules/@mongosh/service-provider-node-driver/node_modules/@mongosh/errors": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@mongosh/errors/-/errors-2.4.2.tgz", + "integrity": "sha512-p+LOHVj/VIt6cpJY1AvDmG/QLP7WZQ7q+32paU6qxjXaoC0kMqmemaqK5cUj8JWod1VEv9/Ol4T6OfWTwPG20A==", + "license": "Apache-2.0", + "engines": { + "node": ">=14.15.1" + } + }, "node_modules/@mongosh/service-provider-node-driver/node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3038,6 +2908,12 @@ "node": ">= 0.8.0" } }, + "node_modules/@mongosh/service-provider-node-driver/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, "node_modules/@mongosh/service-provider-node-driver/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -3350,9 +3226,9 @@ } }, "node_modules/@pkgr/core": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", - "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, "license": "MIT", "engines": { @@ -4257,9 +4133,9 @@ "license": "MIT" }, "node_modules/@redocly/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.3.tgz", + "integrity": "sha512-4P3iZse91TkBiY+Dx5DUgxQ9GXkVJf++cmI0MOyLDxV9b5MUBI4II6ES8zA5JCbO72nKAJxWrw4PUPW+YP3ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4274,9 +4150,9 @@ } }, "node_modules/@redocly/cli": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.0.7.tgz", - "integrity": "sha512-n4r3spiDwNetQ1TmIKaiIUO4d5rXhbzmz1PgOpupYIo6XmE7LQvrX5ncG0qhP6Wl6rKdLoyQZ1bOwbRz2ce3qg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.0.8.tgz", + "integrity": "sha512-FWBhB2hvF8rXWViCVgbKT3AmQH9ChRDNCmN3SyLRaL0GQ5SLi10p9rV26/7GktcMAKetM9XC4ETQQJMup3CVQQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4284,8 +4160,8 @@ "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-node": "2.0.1", "@opentelemetry/semantic-conventions": "1.34.0", - "@redocly/openapi-core": "2.0.7", - "@redocly/respect-core": "2.0.7", + "@redocly/openapi-core": "2.0.8", + "@redocly/respect-core": "2.0.8", "abort-controller": "^3.0.0", "chokidar": "^3.5.1", "colorette": "^1.2.0", @@ -4316,94 +4192,153 @@ "npm": ">=10" } }, - "node_modules/@redocly/cli/node_modules/@redocly/config": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.28.0.tgz", - "integrity": "sha512-IdY4bSX9bbjXkDX91oO1OVwCzB00UNF0ozoygacTpS5Exa3ewYCB/6BcbA0tGCAvKDIwSY9Jor2cWQ/ycQfBTg==", + "node_modules/@redocly/cli/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "dependencies": { - "json-schema-to-ts": "2.7.2" + "engines": { + "node": ">=8" } }, - "node_modules/@redocly/cli/node_modules/@redocly/openapi-core": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-2.0.7.tgz", - "integrity": "sha512-OKSqhvJXPu2s4s6xB5ykUYXvAuQNubZcexd9AEdWse6dz8Ke3EqCmMEuAZkpFWaiunBReu7Wam8kE0vh4CrLBg==", + "node_modules/@redocly/cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@redocly/ajv": "^8.11.2", - "@redocly/config": "^0.28.0", - "ajv-formats": "^2.1.1", - "colorette": "^1.2.0", - "js-levenshtein": "^1.1.6", - "js-yaml": "^4.1.0", - "minimatch": "^10.0.1", - "pluralize": "^8.0.0", - "yaml-ast-parser": "0.0.43" - }, + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/@redocly/cli/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/@redocly/cli/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": ">=22.12.0 || >=20.19.0 <21.0.0", - "npm": ">=10" + "node": ">=8" } }, - "node_modules/@redocly/cli/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "node_modules/@redocly/cli/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": "ISC", + "license": "MIT", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": "20 || >=22" + "node": ">=8" + } + }, + "node_modules/@redocly/cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@redocly/cli/node_modules/yargs": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@redocly/cli/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" } }, "node_modules/@redocly/config": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", - "integrity": "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.29.0.tgz", + "integrity": "sha512-AkP1Berx9GvD15aN6r0IcOo289ElHp52XgeFTxXCumJ4gaUXUmvzqfZTfFFJWDaGgZvUZvmQrs1UdaPjZEXeHA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "2.7.2" + } }, "node_modules/@redocly/openapi-core": { - "version": "1.34.4", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.4.tgz", - "integrity": "sha512-hf53xEgpXIgWl3b275PgZU3OTpYh1RoD2LHdIfQ1JzBNTWsiNKczTEsI/4Tmh2N1oq9YcphhSMyk3lDh85oDjg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-2.0.8.tgz", + "integrity": "sha512-ShnpeEgcQY0YxopFDH/m94PWiHgCuWGa9FIcHchdHMZGA0mgV/Eojhi46A/hi2KP7TaVsgZc6+8u7GxTTeTC5g==", "dev": true, "license": "MIT", "dependencies": { "@redocly/ajv": "^8.11.2", - "@redocly/config": "^0.22.0", + "@redocly/config": "^0.29.0", + "ajv-formats": "^2.1.1", "colorette": "^1.2.0", - "https-proxy-agent": "^7.0.5", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", - "minimatch": "^5.0.1", + "minimatch": "^10.0.1", "pluralize": "^8.0.0", "yaml-ast-parser": "0.0.43" }, "engines": { - "node": ">=18.17.0", - "npm": ">=9.5.0" + "node": ">=22.12.0 || >=20.19.0 <21.0.0", + "npm": ">=10" } }, "node_modules/@redocly/respect-core": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@redocly/respect-core/-/respect-core-2.0.7.tgz", - "integrity": "sha512-a3zY4baRzhdgCdcBdzSKJzHmq8JudniSjEyX/mgwZrq8IWcCyQ7RlgBmtnT1cXp0ebq59Mh8UGp3Ec7gUo/NWg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@redocly/respect-core/-/respect-core-2.0.8.tgz", + "integrity": "sha512-gKOjUn/UmoHYIDKHV7RanAvFkhQIgQh9oiA3qfYHMI/N4+tWGoJd03Soc+2bn3Zie8DTXjKwQIy0k5n2kZA7hw==", "dev": true, "license": "MIT", "dependencies": { "@faker-js/faker": "^7.6.0", "@noble/hashes": "^1.8.0", "@redocly/ajv": "8.11.2", - "@redocly/openapi-core": "2.0.7", + "@redocly/openapi-core": "2.0.8", "better-ajv-errors": "^1.2.0", "colorette": "^2.0.20", "jest-diff": "^29.3.1", @@ -4418,45 +4353,23 @@ "npm": ">=10" } }, - "node_modules/@redocly/respect-core/node_modules/@redocly/config": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.28.0.tgz", - "integrity": "sha512-IdY4bSX9bbjXkDX91oO1OVwCzB00UNF0ozoygacTpS5Exa3ewYCB/6BcbA0tGCAvKDIwSY9Jor2cWQ/ycQfBTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-schema-to-ts": "2.7.2" - } - }, - "node_modules/@redocly/respect-core/node_modules/@redocly/openapi-core": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-2.0.7.tgz", - "integrity": "sha512-OKSqhvJXPu2s4s6xB5ykUYXvAuQNubZcexd9AEdWse6dz8Ke3EqCmMEuAZkpFWaiunBReu7Wam8kE0vh4CrLBg==", + "node_modules/@redocly/respect-core/node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", "dev": true, "license": "MIT", "dependencies": { - "@redocly/ajv": "^8.11.2", - "@redocly/config": "^0.28.0", - "ajv-formats": "^2.1.1", - "colorette": "^1.2.0", - "js-levenshtein": "^1.1.6", - "js-yaml": "^4.1.0", - "minimatch": "^10.0.1", - "pluralize": "^8.0.0", - "yaml-ast-parser": "0.0.43" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" }, - "engines": { - "node": ">=22.12.0 || >=20.19.0 <21.0.0", - "npm": ">=10" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@redocly/respect-core/node_modules/@redocly/openapi-core/node_modules/colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true, - "license": "MIT" - }, "node_modules/@redocly/respect-core/node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -4464,26 +4377,10 @@ "dev": true, "license": "MIT" }, - "node_modules/@redocly/respect-core/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz", + "integrity": "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==", "cpu": [ "arm" ], @@ -4495,9 +4392,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz", + "integrity": "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==", "cpu": [ "arm64" ], @@ -4509,9 +4406,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz", + "integrity": "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==", "cpu": [ "arm64" ], @@ -4523,9 +4420,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz", + "integrity": "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==", "cpu": [ "x64" ], @@ -4537,9 +4434,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz", + "integrity": "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==", "cpu": [ "arm64" ], @@ -4551,9 +4448,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz", + "integrity": "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==", "cpu": [ "x64" ], @@ -4565,9 +4462,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz", + "integrity": "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==", "cpu": [ "arm" ], @@ -4579,9 +4476,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz", + "integrity": "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==", "cpu": [ "arm" ], @@ -4593,9 +4490,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz", + "integrity": "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==", "cpu": [ "arm64" ], @@ -4607,9 +4504,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz", + "integrity": "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==", "cpu": [ "arm64" ], @@ -4621,9 +4518,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz", + "integrity": "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==", "cpu": [ "loong64" ], @@ -4634,10 +4531,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz", + "integrity": "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==", "cpu": [ "ppc64" ], @@ -4649,9 +4546,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz", + "integrity": "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==", "cpu": [ "riscv64" ], @@ -4663,9 +4560,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz", + "integrity": "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==", "cpu": [ "riscv64" ], @@ -4677,9 +4574,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz", + "integrity": "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==", "cpu": [ "s390x" ], @@ -4691,9 +4588,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz", + "integrity": "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==", "cpu": [ "x64" ], @@ -4705,9 +4602,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz", + "integrity": "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==", "cpu": [ "x64" ], @@ -4718,10 +4615,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz", + "integrity": "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz", + "integrity": "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==", "cpu": [ "arm64" ], @@ -4733,9 +4644,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz", + "integrity": "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==", "cpu": [ "ia32" ], @@ -4747,9 +4658,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz", + "integrity": "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==", "cpu": [ "x64" ], @@ -4768,12 +4679,12 @@ "license": "MIT" }, "node_modules/@smithy/abort-controller": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", - "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.5.tgz", + "integrity": "sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -4781,15 +4692,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", - "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.5.tgz", + "integrity": "sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/types": "^4.3.2", "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", + "@smithy/util-middleware": "^4.0.5", "tslib": "^2.6.2" }, "engines": { @@ -4797,35 +4708,50 @@ } }, "node_modules/@smithy/core": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.7.0.tgz", - "integrity": "sha512-7ov8hu/4j0uPZv8b27oeOFtIBtlFmM3ibrPv/Omx1uUdoXvcpJ00U+H/OWWC/keAguLlcqwtyL2/jTlSnApgNQ==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.9.2.tgz", + "integrity": "sha512-H7H+dnfyHa/XXmZB3+IcqB1snIvbXaeGbV7//PMY69YKMOfGtuHPg6aukxsD0TyqmIU+bcX5nitR+nf/19nTlQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.0.8", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@smithy/middleware-serde": "^4.0.9", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-stream": "^4.2.3", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-stream": "^4.2.4", "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, "engines": { "node": ">=18.0.0" } }, + "node_modules/@smithy/core/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", - "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.7.tgz", + "integrity": "sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", "tslib": "^2.6.2" }, "engines": { @@ -4833,14 +4759,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.0.tgz", - "integrity": "sha512-mADw7MS0bYe2OGKkHYMaqarOXuDwRbO6ArD91XhHcl2ynjGCFF+hvqf0LyQcYxkA1zaWjefSkU7Ne9mqgApSgQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.1.tgz", + "integrity": "sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/querystring-builder": "^4.0.5", + "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" }, @@ -4849,12 +4775,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", - "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.5.tgz", + "integrity": "sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" @@ -4864,12 +4790,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", - "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.5.tgz", + "integrity": "sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -4889,13 +4815,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", - "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.5.tgz", + "integrity": "sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -4903,18 +4829,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.1.14", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.14.tgz", - "integrity": "sha512-+BGLpK5D93gCcSEceaaYhUD/+OCGXM1IDaq/jKUQ+ujB0PTWlWN85noodKw/IPFZhIKFCNEe19PGd/reUMeLSQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.7.0", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-middleware": "^4.0.4", + "version": "4.1.21", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.21.tgz", + "integrity": "sha512-VCFE6LGSbnXs6uxLTdtar6dbkOHa9mrj692pZJx1mQVEzk0gvckAX9WB9BzlONUpv92QBWGezROz/+yEitQjAQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.9.2", + "@smithy/middleware-serde": "^4.0.9", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", + "@smithy/util-middleware": "^4.0.5", "tslib": "^2.6.2" }, "engines": { @@ -4922,18 +4848,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.15.tgz", - "integrity": "sha512-iKYUJpiyTQ33U2KlOZeUb0GwtzWR3C0soYcKuCnTmJrvt6XwTPQZhMfsjJZNw7PpQ3TU4Ati1qLSrkSJxnnSMQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/protocol-http": "^5.1.2", - "@smithy/service-error-classification": "^4.0.6", - "@smithy/smithy-client": "^4.4.6", - "@smithy/types": "^4.3.1", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", + "version": "4.1.22", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.22.tgz", + "integrity": "sha512-mb6/wn4ixnSJCkKVLs51AKAyknbSTvwrHCM7cqgwGfYQ7/J6Qvv+49cBHe6Rl8Q0m3fROVYcSvM6bBiQtuhYWg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.4", + "@smithy/protocol-http": "^5.1.3", + "@smithy/service-error-classification": "^4.0.7", + "@smithy/smithy-client": "^4.5.2", + "@smithy/types": "^4.3.2", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-retry": "^4.0.7", + "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -4955,13 +4882,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", - "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.9.tgz", + "integrity": "sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -4969,12 +4896,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", - "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.5.tgz", + "integrity": "sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -4982,14 +4909,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", - "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.4.tgz", + "integrity": "sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -4997,15 +4924,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.0.tgz", - "integrity": "sha512-vqfSiHz2v8b3TTTrdXi03vNz1KLYYS3bhHCDv36FYDqxT7jvTll1mMnCrkD+gOvgwybuunh/2VmvOMqwBegxEg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.1.tgz", + "integrity": "sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", + "@smithy/abort-controller": "^4.0.5", + "@smithy/protocol-http": "^5.1.3", + "@smithy/querystring-builder": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5013,12 +4940,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", - "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.5.tgz", + "integrity": "sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5026,12 +4953,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", - "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.3.tgz", + "integrity": "sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5039,12 +4966,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", - "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.5.tgz", + "integrity": "sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" }, @@ -5053,12 +4980,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", - "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.5.tgz", + "integrity": "sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5066,24 +4993,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.6.tgz", - "integrity": "sha512-RRoTDL//7xi4tn5FrN2NzH17jbgmnKidUqd4KvquT0954/i6CXXkh1884jBiunq24g9cGtPBEXlU40W6EpNOOg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.7.tgz", + "integrity": "sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1" + "@smithy/types": "^4.3.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", - "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.5.tgz", + "integrity": "sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5091,16 +5018,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", - "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.3.tgz", + "integrity": "sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", + "@smithy/util-middleware": "^4.0.5", "@smithy/util-uri-escape": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" @@ -5110,17 +5037,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.6.tgz", - "integrity": "sha512-3wfhywdzB/CFszP6moa5L3lf5/zSfQoH0kvVSdkyK2az5qZet0sn2PAHjcTDiq296Y4RP5yxF7B6S6+3oeBUCQ==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.5.2.tgz", + "integrity": "sha512-WRdTJ7aNSJY0WuGpxrvVgRaFKGiuvtXX1Txhnu2BdynraSlH2bcP75riQ4SiQfawU1HNEKaPI5gf/ePm+Ro/Cw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.7.0", - "@smithy/middleware-endpoint": "^4.1.14", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.3", + "@smithy/core": "^3.9.2", + "@smithy/middleware-endpoint": "^4.1.21", + "@smithy/middleware-stack": "^4.0.5", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "@smithy/util-stream": "^4.2.4", "tslib": "^2.6.2" }, "engines": { @@ -5128,9 +5055,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", - "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.2.tgz", + "integrity": "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -5140,13 +5067,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", - "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.5.tgz", + "integrity": "sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.0.4", - "@smithy/types": "^4.3.1", + "@smithy/querystring-parser": "^4.0.5", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5217,14 +5144,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.22", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.22.tgz", - "integrity": "sha512-hjElSW18Wq3fUAWVk6nbk7pGrV7ZT14DL1IUobmqhV3lxcsIenr5FUsDe2jlTVaS8OYBI3x+Og9URv5YcKb5QA==", + "version": "4.0.29", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.29.tgz", + "integrity": "sha512-awrIb21sWml3OMRhqf8e5GPLuZAcH3PRAHXVOPof/rBOKLxc6N01ZRs25154Ww6Ygm9oNP6G0tVvhcy8ktYXtw==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.6", - "@smithy/types": "^4.3.1", + "@smithy/property-provider": "^4.0.5", + "@smithy/smithy-client": "^4.5.2", + "@smithy/types": "^4.3.2", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -5233,17 +5160,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.22", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.22.tgz", - "integrity": "sha512-7B8mfQBtwwr2aNRRmU39k/bsRtv9B6/1mTMrGmmdJFKmLAH+KgIiOuhaqfKOBGh9sZ/VkZxbvm94rI4MMYpFjQ==", + "version": "4.0.29", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.29.tgz", + "integrity": "sha512-DxBWCC059GwOQXc5nxVudhdGQLZHTDhU4rkK4rvaBQn8IWBw8G+3H2hWk897LaNv6zwwhh7kpfqF0rJ77DvlSg==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.1.4", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/smithy-client": "^4.4.6", - "@smithy/types": "^4.3.1", + "@smithy/config-resolver": "^4.1.5", + "@smithy/credential-provider-imds": "^4.0.7", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/property-provider": "^4.0.5", + "@smithy/smithy-client": "^4.5.2", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5251,13 +5178,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", - "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.7.tgz", + "integrity": "sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5277,12 +5204,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", - "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.5.tgz", + "integrity": "sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.1", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5290,13 +5217,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.6.tgz", - "integrity": "sha512-+YekoF2CaSMv6zKrA6iI/N9yva3Gzn4L6n35Luydweu5MMPYpiGZlWqehPHDHyNbnyaYlz/WJyYAZnC+loBDZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.7.tgz", + "integrity": "sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.0.6", - "@smithy/types": "^4.3.1", + "@smithy/service-error-classification": "^4.0.7", + "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -5304,14 +5231,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.3.tgz", - "integrity": "sha512-cQn412DWHHFNKrQfbHY8vSFI3nTROY1aIKji9N0tpp8gUABRilr7wdf8fqBbSlXresobM+tQFNk6I+0LXK/YZg==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.4.tgz", + "integrity": "sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.1.0", - "@smithy/node-http-handler": "^4.1.0", - "@smithy/types": "^4.3.1", + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", @@ -5488,13 +5415,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.0.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz", - "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", + "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.8.0" + "undici-types": "~7.10.0" } }, "node_modules/@types/proper-lockfile": { @@ -5529,9 +5456,9 @@ "license": "MIT" }, "node_modules/@types/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", "dev": true, "license": "MIT" }, @@ -5580,6 +5507,12 @@ "license": "MIT", "optional": true }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -5603,17 +5536,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", - "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz", + "integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/type-utils": "8.38.0", - "@typescript-eslint/utils": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.42.0", + "@typescript-eslint/type-utils": "8.42.0", + "@typescript-eslint/utils": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -5627,9 +5560,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.38.0", + "@typescript-eslint/parser": "^8.42.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -5643,16 +5576,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", - "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz", + "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.42.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0", "debug": "^4.3.4" }, "engines": { @@ -5664,17 +5597,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", - "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz", + "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.38.0", - "@typescript-eslint/types": "^8.38.0", + "@typescript-eslint/tsconfig-utils": "^8.42.0", + "@typescript-eslint/types": "^8.42.0", "debug": "^4.3.4" }, "engines": { @@ -5685,17 +5618,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", - "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz", + "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0" + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5706,9 +5639,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", - "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz", + "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5718,19 +5651,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", - "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz", + "integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0", + "@typescript-eslint/utils": "8.42.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -5743,13 +5676,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz", + "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5760,15 +5693,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", - "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz", + "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.38.0", - "@typescript-eslint/tsconfig-utils": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/project-service": "8.42.0", + "@typescript-eslint/tsconfig-utils": "8.42.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -5784,7 +5717,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { @@ -5803,15 +5745,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", - "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz", + "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0" + "@typescript-eslint/scope-manager": "8.42.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5822,16 +5764,16 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", - "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz", + "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/types": "8.42.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5842,6 +5784,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vitest/coverage-v8": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", @@ -5877,11 +5831,12 @@ } }, "node_modules/@vitest/eslint-plugin": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.3.4.tgz", - "integrity": "sha512-EOg8d0jn3BAiKnR55WkFxmxfWA3nmzrbIIuOXyTe6A72duryNgyU+bdBEauA97Aab3ho9kLmAwgPX63Ckj4QEg==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.3.8.tgz", + "integrity": "sha512-+M0eRDo/UiIF4xZZbZBBAR2Resx0ihdLRNpYevkrDJ6F3xHuEXSAAJGt6Ahabd0eJC4mQKvLA1JY1qBM058Cag==", "license": "MIT", "dependencies": { + "@typescript-eslint/scope-manager": "^8.41.0", "@typescript-eslint/utils": "^8.24.1" }, "peerDependencies": { @@ -6083,9 +6038,9 @@ } }, "node_modules/ai": { - "version": "4.3.17", - "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.17.tgz", - "integrity": "sha512-uWqIQ94Nb1GTYtYElGHegJMOzv3r2mCKNFlKrqkft9xrfvIahTI5OdcnD5U9612RFGuUNGmSDTO1/YRNFXobaQ==", + "version": "4.3.19", + "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.19.tgz", + "integrity": "sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -6177,13 +6132,16 @@ } }, "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==", - "devOptional": true, + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { @@ -6241,24 +6199,61 @@ "node": ">=10" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "license": "MIT", "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "devOptional": true, "license": "MIT", @@ -6279,17 +6274,26 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.5.tgz", + "integrity": "sha512-9SdXjNheSiE8bALAQCQQuT6fgQaoxJh7IRYrRGZ8/9nv8WhJeC1aXAwN8TbaOssGOukUvyvnkgD9+Yuykvl1aA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.30", "estree-walker": "^3.0.3", "js-tokens": "^9.0.1" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6301,7 +6305,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -6488,9 +6491,9 @@ } }, "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", + "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", "license": "MIT" }, "node_modules/bplist-parser": { @@ -6506,12 +6509,13 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -6536,10 +6540,10 @@ } }, "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "devOptional": true, + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, "funding": [ { "type": "github", @@ -6557,7 +6561,7 @@ "license": "MIT", "dependencies": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "node_modules/buffer-alloc": { @@ -6642,7 +6646,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", @@ -6713,9 +6716,9 @@ } }, "node_modules/chai": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", - "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -6830,15 +6833,81 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "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==", + "devOptional": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.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==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "devOptional": true, + "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==", + "devOptional": 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/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==", + "devOptional": true, + "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==", + "devOptional": 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/clsx": { @@ -6933,19 +7002,18 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.0.tgz", - "integrity": "sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.2", - "lodash": "^4.17.21", - "rxjs": "^7.8.1", - "shell-quote": "^1.8.1", - "supports-color": "^8.1.1", - "tree-kill": "^1.2.2", - "yargs": "^17.7.2" + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", @@ -6958,21 +7026,6 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/concurrently/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/concurrently/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -6989,35 +7042,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/concurrently/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/concurrently/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/content-disposition": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", @@ -7058,9 +7082,9 @@ } }, "node_modules/core-js": { - "version": "3.44.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.44.0.tgz", - "integrity": "sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==", + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz", + "integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7163,6 +7187,57 @@ "node": ">= 12" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -7386,7 +7461,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -7412,6 +7486,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/degenerator": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", @@ -7556,54 +7647,12 @@ "readable-stream": "^4.5.2" } }, - "node_modules/duplexpair/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/duplexpair/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "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" + "license": "MIT" }, "node_modules/ee-first": { "version": "1.1.1", @@ -7612,10 +7661,10 @@ "license": "MIT" }, "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==", - "devOptional": true, + "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": { @@ -7637,6 +7686,74 @@ "once": "^1.4.0" } }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -7678,7 +7795,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -7690,6 +7806,23 @@ "node": ">= 0.4" } }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es6-promise": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", @@ -7698,9 +7831,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "devOptional": true, "hasInstallScript": true, "license": "MIT", @@ -7711,32 +7844,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/escalade": { @@ -7789,19 +7922,19 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", + "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.35.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -7849,9 +7982,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -7865,9 +7998,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", - "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, "license": "MIT", "dependencies": { @@ -7912,6 +8045,18 @@ } }, "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", @@ -7923,16 +8068,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -7974,6 +8109,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -8088,12 +8235,12 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz", - "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==", + "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": ">=20.0.0" + "node": ">=18.0.0" } }, "node_modules/execa": { @@ -8143,6 +8290,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -8255,12 +8408,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "license": "MIT" }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "license": "MIT" - }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -8269,9 +8416,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "dev": true, "funding": [ { @@ -8286,9 +8433,10 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "dev": true, "funding": [ { "type": "github", @@ -8297,7 +8445,7 @@ ], "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" @@ -8442,7 +8590,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -8478,19 +8625,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", @@ -8618,6 +8752,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -8689,6 +8852,23 @@ "node": ">=0.10.0" } }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-tsconfig": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", @@ -8768,65 +8948,6 @@ "node": ">= 6" } }, - "node_modules/glob/node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/glob/node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/globals": { "version": "16.3.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", @@ -8840,6 +8961,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -8888,6 +9025,18 @@ "uglify-js": "^3.1.4" } }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8901,7 +9050,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -8910,6 +9058,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -8926,7 +9089,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -9126,6 +9288,20 @@ "license": "ISC", "optional": true }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", @@ -9155,8 +9331,59 @@ "license": "MIT", "optional": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, @@ -9168,11 +9395,26 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -9181,6 +9423,39 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -9205,6 +9480,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "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", @@ -9215,6 +9505,24 @@ "node": ">=8" } }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -9245,6 +9553,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-natural-number": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", @@ -9252,6 +9572,18 @@ "dev": true, "license": "MIT" }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9261,12 +9593,73 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "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-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -9277,11 +9670,43 @@ "node": ">=0.10.0" } }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typed-array": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" @@ -9293,6 +9718,49 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", @@ -9312,7 +9780,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, "license": "MIT" }, "node_modules/isexe": { @@ -9369,9 +9836,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9383,19 +9850,19 @@ } }, "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==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/jest-diff": { @@ -9441,9 +9908,9 @@ } }, "node_modules/jose": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.12.tgz", - "integrity": "sha512-T8xypXs8CpmiIi78k0E+Lk7T2zlK4zDyg+o1CZ4AkOHgDg98ogdP2BeZ61lTFKFyoEwJ9RgAgN+SdM3iPgNonQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.0.tgz", + "integrity": "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==", "license": "MIT", "peer": true, "funding": { @@ -9565,9 +10032,9 @@ } }, "node_modules/jsondiffpatch/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", + "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==", "dev": true, "license": "MIT", "engines": { @@ -9668,13 +10135,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -9709,16 +10169,16 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "devOptional": true, "license": "MIT" }, "node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", + "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==", "license": "ISC", "engines": { "node": "20 || >=22" @@ -9764,13 +10224,13 @@ "optional": true }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", "devOptional": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/magicast": { @@ -9960,16 +10420,19 @@ } }, "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -9983,13 +10446,13 @@ } }, "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/minizlib": { @@ -10103,9 +10566,9 @@ } }, "node_modules/mongodb": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz", - "integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.19.0.tgz", + "integrity": "sha512-H3GtYujOJdeKIMLKBT9PwlDhGrQfplABNF1G904w6r5ZXKWyv77aB0X9B+rhmaAwjtllHzaEkvi9mkGVZxs2Bw==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", @@ -10121,7 +10584,7 @@ "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", + "snappy": "^7.3.2", "socks": "^2.7.1" }, "peerDependenciesMeta": { @@ -10158,9 +10621,9 @@ } }, "node_modules/mongodb-client-encryption": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/mongodb-client-encryption/-/mongodb-client-encryption-6.4.0.tgz", - "integrity": "sha512-Un1W/5P4KjcUBPeJeSKFNaWH0/8PVsoSatDqyWM2bMK0Vu2Jjxy7ZTgDj1g+uChuqroB09s8LvppdsHpwxSTVA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/mongodb-client-encryption/-/mongodb-client-encryption-6.5.0.tgz", + "integrity": "sha512-Gj8EeyYKsssdko0NKhWRBGDif6uVFBbv+e+Nyn7E316UmRzApc4IP+p2NLm+av+fU+dFHVT5WqfzaQVDTh8i9w==", "hasInstallScript": true, "license": "Apache-2.0", "optional": true, @@ -10271,10 +10734,13 @@ "optional": true }, "node_modules/mongodb-redact": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/mongodb-redact/-/mongodb-redact-1.1.8.tgz", - "integrity": "sha512-EbZ+q7LsVz7q8n49mGIcXgP2UiBp6R6vHEVbmGnF21ThCnP6AIho7wqpHqyjqqGjg54DoXQJTCwHPSknsCHv6g==", - "license": "Apache-2.0" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mongodb-redact/-/mongodb-redact-1.2.0.tgz", + "integrity": "sha512-UVJBlVNEF/8UhZ/SwR+KJXqf6pVY0b0M9aBa+1cwdRAoFFqH5NZUhMdzaXCCvhY2hoPtZ32Z7vYMoDl6Msmm/g==", + "license": "Apache-2.0", + "dependencies": { + "regexp.escape": "^2.0.1" + } }, "node_modules/mongodb-runner": { "version": "5.9.2", @@ -10294,50 +10760,6 @@ "mongodb-runner": "bin/runner.js" } }, - "node_modules/mongodb-runner/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mongodb-runner/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mongodb-runner/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/mongodb-schema": { "version": "12.6.2", "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", @@ -10361,50 +10783,6 @@ "yargs": "^17.6.2" } }, - "node_modules/mongodb-schema/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", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mongodb-schema/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", - "optional": true, - "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/mongodb-schema/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", - "optional": true, - "engines": { - "node": ">=12" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -10476,9 +10854,9 @@ } }, "node_modules/node-abi": { - "version": "3.75.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", - "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "version": "3.77.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.77.0.tgz", + "integrity": "sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==", "license": "MIT", "optional": true, "dependencies": { @@ -10715,9 +11093,9 @@ } }, "node_modules/oauth4webapi": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.7.0.tgz", - "integrity": "sha512-Q52wTPUWPsVLVVmTViXPQFMW2h2xv2jnDGxypjpelCFKaOjLsm7AxYuOk1oQgFm95VNDbuggasu9htXrz6XwKw==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.1.tgz", + "integrity": "sha512-olkZDELNycOWQf9LrsELFq8n05LwJgV8UkrS0cburk6FOwf8GvLam+YB+Uj5Qvryee+vwWOfQVeI5Vm0MVg7SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -10753,10 +11131,39 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/oidc-token-hash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", - "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz", + "integrity": "sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g==", "license": "MIT", "engines": { "node": "^10.13.0 || >=12.0.0" @@ -10860,38 +11267,6 @@ "json-pointer": "0.6.2" } }, - "node_modules/openapi-sampler/node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.1.1" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/openapi-sampler/node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/openapi-types": { "version": "12.1.3", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", @@ -10900,17 +11275,17 @@ "license": "MIT" }, "node_modules/openapi-typescript": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.8.0.tgz", - "integrity": "sha512-1EeVWmDzi16A+siQlo/SwSGIT7HwaFAVjvMA7/jG5HMLSnrUOzPL7uSTRZZa4v/LCRxHTApHKtNY6glApEoiUQ==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.9.1.tgz", + "integrity": "sha512-9gJtoY04mk6iPMbToPjPxEAtfXZ0dTsMZtsgUI8YZta0btPPig9DJFP4jlerQD/7QOwYgb0tl+zLUpDf7vb7VA==", "dev": true, "license": "MIT", "dependencies": { - "@redocly/openapi-core": "^1.34.3", + "@redocly/openapi-core": "^1.34.5", "ansi-colors": "^4.1.3", "change-case": "^5.4.4", "parse-json": "^8.3.0", - "supports-color": "^10.0.0", + "supports-color": "^10.1.0", "yargs-parser": "^21.1.1" }, "bin": { @@ -10926,38 +11301,80 @@ "integrity": "sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==", "license": "MIT" }, - "node_modules/openapi-typescript/node_modules/supports-color": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.0.0.tgz", - "integrity": "sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==", + "node_modules/openapi-typescript/node_modules/@redocly/config": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", + "integrity": "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/openapi-typescript/node_modules/@redocly/openapi-core": { + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.5.tgz", + "integrity": "sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.22.0", + "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.5", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "minimatch": "^5.0.1", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" } }, - "node_modules/openapi-typescript/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==", + "node_modules/openapi-typescript/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/openapi-typescript/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=10" + } + }, + "node_modules/openapi-typescript/node_modules/supports-color": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.0.tgz", + "integrity": "sha512-5eG9FQjEjDbAlI5+kdpdyPIBMRH4GfTVDGREVupaZHmVoppknhM29b/S9BkQz7cathp85BVgRi/As3Siln7e0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/openid-client": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.6.4.tgz", - "integrity": "sha512-PLWVhRksRnNH05sqeuCX/PR+1J70NyZcAcPske+FeF732KKONd3v0p5Utx1ro1iLfCglH8B3/+dA1vqIHDoIiA==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.7.1.tgz", + "integrity": "sha512-kOiE4q0kNogr90hXsxPrKeEDuY+V0kkZazvZScOwZkYept9slsaQ3usXTaKkm6I04vLNuw5caBoX7UfrwC6x8w==", "license": "MIT", "peer": true, "dependencies": { - "jose": "^6.0.12", - "oauth4webapi": "^3.7.0" + "jose": "^6.1.0", + "oauth4webapi": "^3.8.0" }, "funding": { "url": "https://github.com/sponsors/panva" @@ -10980,6 +11397,12 @@ "node": ">= 0.8.0" } }, + "node_modules/optionator/node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT" + }, "node_modules/os-dns-native": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/os-dns-native/-/os-dns-native-1.2.1.tgz", @@ -11008,6 +11431,23 @@ "dev": true, "license": "MIT" }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -11156,36 +11596,30 @@ } }, "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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "engines": { - "node": ">=16" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/pathe": { @@ -11308,7 +11742,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -11511,6 +11944,13 @@ "signal-exit": "^3.0.2" } }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/protobufjs": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", @@ -11624,18 +12064,34 @@ } }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", - "iconv-lite": "0.6.3", + "iconv-lite": "0.7.0", "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "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/rc": { @@ -11796,18 +12252,20 @@ } }, "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "devOptional": true, + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/readdirp": { @@ -11864,6 +12322,80 @@ "styled-components": "^4.1.1 || ^5.1.1 || ^6.0.5" } }, + "node_modules/redoc/node_modules/@redocly/config": { + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", + "integrity": "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/redoc/node_modules/@redocly/openapi-core": { + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.5.tgz", + "integrity": "sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.22.0", + "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.5", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "minimatch": "^5.0.1", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + } + }, + "node_modules/redoc/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/redoc/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/reftools": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", @@ -11874,6 +12406,46 @@ "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, + "node_modules/regexp.escape": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexp.escape/-/regexp.escape-2.0.1.tgz", + "integrity": "sha512-JItRb4rmyTzmERBkAf6J87LjDPy/RscIwmaJQ3gsFlAzrmZbZU8LwBw5IydFZXW9hqpgbPlGbMhtpqtuAhMgtg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "for-each": "^0.3.3", + "safe-regex-test": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -11953,9 +12525,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.0.tgz", + "integrity": "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -11969,26 +12541,27 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.50.0", + "@rollup/rollup-android-arm64": "4.50.0", + "@rollup/rollup-darwin-arm64": "4.50.0", + "@rollup/rollup-darwin-x64": "4.50.0", + "@rollup/rollup-freebsd-arm64": "4.50.0", + "@rollup/rollup-freebsd-x64": "4.50.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.0", + "@rollup/rollup-linux-arm-musleabihf": "4.50.0", + "@rollup/rollup-linux-arm64-gnu": "4.50.0", + "@rollup/rollup-linux-arm64-musl": "4.50.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.50.0", + "@rollup/rollup-linux-ppc64-gnu": "4.50.0", + "@rollup/rollup-linux-riscv64-gnu": "4.50.0", + "@rollup/rollup-linux-riscv64-musl": "4.50.0", + "@rollup/rollup-linux-s390x-gnu": "4.50.0", + "@rollup/rollup-linux-x64-gnu": "4.50.0", + "@rollup/rollup-linux-x64-musl": "4.50.0", + "@rollup/rollup-openharmony-arm64": "4.50.0", + "@rollup/rollup-win32-arm64-msvc": "4.50.0", + "@rollup/rollup-win32-ia32-msvc": "4.50.0", + "@rollup/rollup-win32-x64-msvc": "4.50.0", "fsevents": "~2.3.2" } }, @@ -12053,6 +12626,25 @@ "tslib": "^2.1.0" } }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -12073,6 +12665,39 @@ ], "license": "MIT" }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -12167,17 +12792,6 @@ "range-parser": "1.2.0" } }, - "node_modules/serve-handler/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/serve-handler/node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -12277,7 +12891,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -12291,6 +12904,35 @@ "node": ">= 0.4" } }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -12478,10 +13120,17 @@ "license": "ISC" }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/simple-concat": { "version": "1.0.1", @@ -12574,6 +13223,21 @@ "ws": "^7.4.2" } }, + "node_modules/simple-websocket/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/simple-websocket/node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", @@ -12617,12 +13281,12 @@ } }, "node_modules/socks": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", - "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -12644,6 +13308,15 @@ "node": ">= 14" } }, + "node_modules/socks/node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -12691,9 +13364,9 @@ "license": "BSD-3-Clause" }, "node_modules/ssh2": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", - "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", + "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", "hasInstallScript": true, "dependencies": { "asn1": "^0.2.6", @@ -12704,7 +13377,7 @@ }, "optionalDependencies": { "cpu-features": "~0.0.10", - "nan": "^2.20.0" + "nan": "^2.23.0" } }, "node_modules/stackback": { @@ -12749,6 +13422,19 @@ "integrity": "sha512-GCp7vHAfpao+Qh/3Flh9DXEJ/qSi0KJwJw6zYlZOtRYXWUIpMM6mC2rIep/dK8RQqwW0KxGJIllmjPIBOGN8AA==", "dev": true }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -12760,18 +13446,21 @@ } }, "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==", - "devOptional": true, + "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": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { @@ -12790,11 +13479,28 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "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==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12803,6 +13509,78 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "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", @@ -12817,6 +13595,16 @@ "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-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", @@ -12865,9 +13653,10 @@ } }, "node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "dev": true, "funding": [ { "type": "github", @@ -13016,9 +13805,9 @@ } }, "node_modules/swr": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", - "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz", + "integrity": "sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==", "dev": true, "license": "MIT", "dependencies": { @@ -13030,13 +13819,13 @@ } }, "node_modules/synckit": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", - "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", "dev": true, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.2.4" + "@pkgr/core": "^0.2.9" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -13066,24 +13855,6 @@ "url": "https://github.com/sponsors/dcastil" } }, - "node_modules/tailwindcss": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz", - "integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/tailwindcss-animate": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", - "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders" - } - }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -13127,6 +13898,31 @@ "readable-stream": "^3.4.0" } }, + "node_modules/tar-fs/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/tar-fs/node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -13134,6 +13930,21 @@ "license": "ISC", "optional": true }, + "node_modules/tar-fs/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/tar-fs/node_modules/tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", @@ -13210,6 +14021,16 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, "node_modules/test-exclude": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", @@ -13225,6 +14046,16 @@ "node": ">=18" } }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/test-exclude/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -13246,6 +14077,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/test-exclude/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/test-exclude/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/test-exclude/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -13262,14 +14116,21 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/test-exclude/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": "ISC", + "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.17" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/throttleit": { @@ -13307,14 +14168,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "devOptional": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -13324,11 +14185,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "devOptional": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -13339,9 +14203,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "devOptional": true, "license": "MIT", "engines": { @@ -13470,6 +14334,12 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-levenshtein": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ts-levenshtein/-/ts-levenshtein-1.0.7.tgz", + "integrity": "sha512-wautEf7gl2ITJuRTTYxnlrLjzUUcwFSdg46bcu4RlzoE/zQM++TJjBFRf2Xhil49GiHqKCqmpjf1lBkWnAHj0A==", + "license": "MIT" + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -13521,9 +14391,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", - "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "version": "4.20.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", + "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", "dev": true, "license": "MIT", "dependencies": { @@ -13602,7 +14472,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.3", @@ -13613,10 +14482,70 @@ "node": ">= 0.4" } }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -13627,16 +14556,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", - "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.42.0.tgz", + "integrity": "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.38.0", - "@typescript-eslint/parser": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0" + "@typescript-eslint/eslint-plugin": "8.42.0", + "@typescript-eslint/parser": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0", + "@typescript-eslint/utils": "8.42.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -13647,7 +14576,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/uglify-js": { @@ -13664,6 +14593,24 @@ "node": ">=0.8.0" } }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", @@ -13675,6 +14622,31 @@ "through": "^2.3.8" } }, + "node_modules/unbzip2-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/undici": { "version": "6.21.3", "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", @@ -13686,9 +14658,9 @@ } }, "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "dev": true, "license": "MIT" }, @@ -13835,18 +14807,18 @@ } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", "devOptional": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", "postcss": "^8.5.6", - "rollup": "^4.40.0", - "tinyglobby": "^0.2.14" + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" @@ -13933,11 +14905,14 @@ } }, "node_modules/vite/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "devOptional": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -13948,9 +14923,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "devOptional": true, "license": "MIT", "engines": { @@ -14063,9 +15038,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "devOptional": true, "license": "MIT", "engines": { @@ -14121,11 +15096,74 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-typed-array": { "version": "1.1.19", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", @@ -14199,18 +15237,18 @@ "license": "MIT" }, "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==", - "devOptional": true, + "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": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -14235,6 +15273,64 @@ "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/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/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -14305,9 +15401,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "dev": true, "license": "ISC", "bin": { @@ -14325,41 +15421,76 @@ "license": "Apache-2.0" }, "node_modules/yargs": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", - "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", - "dev": true, + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "devOptional": true, "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "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": "^20.19.0 || ^22.12.0 || >=23" + "node": ">=12" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "license": "ISC", + "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==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">=10" + "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==", + "devOptional": true, + "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==", + "devOptional": 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/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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/yauzl": { diff --git a/package.json b/package.json index e26add686..c6da0bcbe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mongodb-mcp-server", "description": "MongoDB Model Context Protocol Server", - "version": "0.3.0", + "version": "1.0.0", "type": "module", "exports": { ".": { @@ -61,13 +61,13 @@ "@ai-sdk/azure": "^1.3.24", "@ai-sdk/google": "^1.2.22", "@ai-sdk/openai": "^1.3.23", - "@eslint/js": "^9.30.1", + "@eslint/js": "^9.34.0", "@modelcontextprotocol/inspector": "^0.16.5", "@mongodb-js/oidc-mock-provider": "^0.11.3", - "@redocly/cli": "^2.0.7", - "@types/express": "^5.0.1", + "@redocly/cli": "^2.0.8", + "@types/express": "^5.0.3", "@types/http-proxy": "^1.17.16", - "@types/node": "^24.0.12", + "@types/node": "^24.3.0", "@types/proper-lockfile": "^4.1.4", "@types/semver": "^7.7.0", "@types/simple-oauth2": "^5.0.7", @@ -75,46 +75,47 @@ "@vitest/coverage-v8": "^3.2.4", "ai": "^4.3.17", "duplexpair": "^1.0.2", - "eslint": "^9.30.1", - "eslint-config-prettier": "^10.1.5", - "eslint-plugin-prettier": "^5.5.1", + "eslint": "^9.34.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", "globals": "^16.3.0", "mongodb-runner": "^5.9.2", "ollama-ai-provider": "^1.2.0", "openapi-types": "^12.1.3", - "openapi-typescript": "^7.8.0", + "openapi-typescript": "^7.9.1", "prettier": "^3.6.2", "proper-lockfile": "^4.1.2", "semver": "^7.7.2", "simple-git": "^3.28.0", - "tsx": "^4.20.3", - "typescript": "^5.8.3", - "typescript-eslint": "^8.36.0", + "tsx": "^4.20.5", + "typescript": "^5.9.2", + "typescript-eslint": "^8.41.0", "uuid": "^11.1.0", "vitest": "^3.2.4", - "yaml": "^2.8.0" + "yaml": "^2.8.1" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.15.0", + "@modelcontextprotocol/sdk": "^1.17.4", "@mongodb-js/device-id": "^0.3.1", - "@mongodb-js/devtools-connect": "^3.9.2", - "@mongodb-js/devtools-proxy-support": "^0.5.1", + "@mongodb-js/devtools-connect": "^3.9.3", + "@mongodb-js/devtools-proxy-support": "^0.5.2", "@mongosh/arg-parser": "^3.14.0", - "@mongosh/service-provider-node-driver": "^3.10.2", + "@mongosh/service-provider-node-driver": "~3.12.0", "@vitest/eslint-plugin": "^1.3.4", "bson": "^6.10.4", "express": "^5.1.0", "lru-cache": "^11.1.0", - "mongodb": "^6.17.0", + "mongodb": "^6.19.0", "mongodb-connection-string-url": "^3.0.2", "mongodb-log-writer": "^2.4.1", - "mongodb-redact": "^1.1.8", + "mongodb-redact": "^1.2.0", "mongodb-schema": "^12.6.2", "node-fetch": "^3.3.2", "node-machine-id": "1.1.12", - "oauth4webapi": "^3.6.0", + "oauth4webapi": "^3.8.0", "openapi-fetch": "^0.14.0", - "yargs-parser": "^22.0.0", + "ts-levenshtein": "^1.0.7", + "yargs-parser": "^21.1.1", "zod": "^3.25.76" }, "engines": { diff --git a/src/common/atlas/accessListUtils.ts b/src/common/atlas/accessListUtils.ts index dbc53df66..34609c3a3 100644 --- a/src/common/atlas/accessListUtils.ts +++ b/src/common/atlas/accessListUtils.ts @@ -22,8 +22,9 @@ export async function makeCurrentIpAccessListEntry( * If the IP is already present, this is a no-op. * @param apiClient The Atlas API client instance * @param projectId The Atlas project ID + * @returns Promise - true if a new IP access list entry was created, false if it already existed */ -export async function ensureCurrentIpInAccessList(apiClient: ApiClient, projectId: string): Promise { +export async function ensureCurrentIpInAccessList(apiClient: ApiClient, projectId: string): Promise { const entry = await makeCurrentIpAccessListEntry(apiClient, projectId, DEFAULT_ACCESS_LIST_COMMENT); try { await apiClient.createProjectIpAccessList({ @@ -35,6 +36,7 @@ export async function ensureCurrentIpInAccessList(apiClient: ApiClient, projectI context: "accessListUtils", message: `IP access list created: ${JSON.stringify(entry)}`, }); + return true; } catch (err) { if (err instanceof ApiClientError && err.response?.status === 409) { // 409 Conflict: entry already exists, log info @@ -43,7 +45,7 @@ export async function ensureCurrentIpInAccessList(apiClient: ApiClient, projectI context: "accessListUtils", message: `IP address ${entry.ipAddress} is already present in the access list for project ${projectId}.`, }); - return; + return false; } apiClient.logger.warning({ id: LogId.atlasIpAccessListAddFailure, @@ -51,4 +53,5 @@ export async function ensureCurrentIpInAccessList(apiClient: ApiClient, projectI message: `Error adding IP access list: ${err instanceof Error ? err.message : String(err)}`, }); } + return false; } diff --git a/src/common/config.ts b/src/common/config.ts index 414e91589..d335fbb43 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -3,6 +3,9 @@ import os from "os"; import argv from "yargs-parser"; import type { CliOptions, ConnectionInfo } from "@mongosh/arg-parser"; import { generateConnectionInfoFromCliArgs } from "@mongosh/arg-parser"; +import { Keychain } from "./keychain.js"; +import type { Secret } from "./keychain.js"; +import levenshtein from "ts-levenshtein"; // From: https://github.com/mongodb-js/mongosh/blob/main/packages/cli-repl/src/arg-parser.ts const OPTIONS = { @@ -45,6 +48,10 @@ const OPTIONS = { "tlsCertificateSelector", "tlsDisabledProtocols", "username", + "atlasTemporaryDatabaseUserLifetimeMs", + "exportsPath", + "exportTimeoutMs", + "exportCleanupIntervalMs", ], boolean: [ "apiDeprecationErrors", @@ -68,7 +75,7 @@ const OPTIONS = { "tlsFIPSMode", "version", ], - array: ["disabledTools", "loggers"], + array: ["disabledTools", "loggers", "confirmationRequiredTools"], alias: { h: "help", p: "password", @@ -87,7 +94,53 @@ const OPTIONS = { "greedy-arrays": true, "short-option-groups": false, }, -}; +} as Readonly; + +interface Options { + string: string[]; + boolean: string[]; + array: string[]; + alias: Record; + configuration: Record; +} + +export const ALL_CONFIG_KEYS = new Set( + (OPTIONS.string as readonly string[]) + .concat(OPTIONS.array) + .concat(OPTIONS.boolean) + .concat(Object.keys(OPTIONS.alias)) +); + +export function validateConfigKey(key: string): { valid: boolean; suggestion?: string } { + if (ALL_CONFIG_KEYS.has(key)) { + return { valid: true }; + } + + let minLev = Number.MAX_VALUE; + let suggestion = ""; + + // find the closest match for a suggestion + for (const validKey of ALL_CONFIG_KEYS) { + // check if there is an exact case-insensitive match + if (validKey.toLowerCase() === key.toLowerCase()) { + return { valid: false, suggestion: validKey }; + } + + // else, infer something using levenshtein so we suggest a valid key + const lev = levenshtein.get(key, validKey); + if (lev < minLev) { + minLev = lev; + suggestion = validKey; + } + } + + if (minLev <= 2) { + // accept up to 2 typos + return { valid: false, suggestion }; + } + + return { valid: false }; +} function isConnectionSpecifier(arg: string | undefined): boolean { return ( @@ -110,7 +163,9 @@ export interface UserConfig extends CliOptions { exportTimeoutMs: number; exportCleanupIntervalMs: number; connectionString?: string; + // TODO: Use a type tracking all tool names. disabledTools: Array; + confirmationRequiredTools: Array; readOnly?: boolean; indexCheck?: boolean; transport: "stdio" | "http"; @@ -120,25 +175,34 @@ export interface UserConfig extends CliOptions { loggers: Array<"stderr" | "disk" | "mcp">; idleTimeoutMs: number; notificationTimeoutMs: number; + atlasTemporaryDatabaseUserLifetimeMs: number; } export const defaultUserConfig: UserConfig = { apiBaseUrl: "https://cloud.mongodb.com/", logPath: getLogPath(), exportsPath: getExportsPath(), - exportTimeoutMs: 300000, // 5 minutes - exportCleanupIntervalMs: 120000, // 2 minutes + exportTimeoutMs: 5 * 60 * 1000, // 5 minutes + exportCleanupIntervalMs: 2 * 60 * 1000, // 2 minutes disabledTools: [], telemetry: "enabled", readOnly: false, indexCheck: false, + confirmationRequiredTools: [ + "atlas-create-access-list", + "atlas-create-db-user", + "drop-database", + "drop-collection", + "delete-many", + ], transport: "stdio", httpPort: 3000, httpHost: "127.0.0.1", loggers: ["disk", "mcp"], - idleTimeoutMs: 600000, // 10 minutes - notificationTimeoutMs: 540000, // 9 minutes + idleTimeoutMs: 10 * 60 * 1000, // 10 minutes + notificationTimeoutMs: 9 * 60 * 1000, // 9 minutes httpHeaders: {}, + atlasTemporaryDatabaseUserLifetimeMs: 4 * 60 * 60 * 1000, // 4 hours }; export const config = setupUserConfig({ @@ -185,12 +249,19 @@ function getExportsPath(): string { // are prefixed with `MDB_MCP_` and the keys match the UserConfig keys, but are converted // to SNAKE_UPPER_CASE. function parseEnvConfig(env: Record): Partial { + const CONFIG_WITH_URLS: Set = new Set<(typeof OPTIONS)["string"][number]>(["connectionString"]); + function setValue(obj: Record, path: string[], value: string): void { const currentField = path.shift(); if (!currentField) { return; } if (path.length === 0) { + if (CONFIG_WITH_URLS.has(currentField)) { + obj[currentField] = value; + return; + } + const numberValue = Number(value); if (!isNaN(numberValue)) { obj[currentField] = numberValue; @@ -247,12 +318,25 @@ function SNAKE_CASE_toCamelCase(str: string): string { // whatever is in mongosh. function parseCliConfig(args: string[]): CliOptions { const programArgs = args.slice(2); - const parsed = argv(programArgs, OPTIONS) as unknown as CliOptions & + const parsed = argv(programArgs, OPTIONS as unknown as argv.Options) as unknown as CliOptions & UserConfig & { _?: string[]; }; const positionalArguments = parsed._ ?? []; + + // we use console.warn here because we still don't have our logging system configured + // so we don't have a logger. For stdio, the warning will be received as a string in + // the client and IDEs like VSCode do show the message in the log window. For HTTP, + // it will be in the stdout of the process. + warnAboutDeprecatedOrUnknownCliArgs( + { ...parsed, _: positionalArguments }, + { + warn: (msg) => console.warn(msg), + exit: (status) => process.exit(status), + } + ); + // if we have a positional argument that matches a connection string // store it as the connection specifier and remove it from the argument // list, so it doesn't get misunderstood by the mongosh args-parser @@ -264,6 +348,49 @@ function parseCliConfig(args: string[]): CliOptions { return parsed; } +export function warnAboutDeprecatedOrUnknownCliArgs( + args: Record, + { warn, exit }: { warn: (msg: string) => void; exit: (status: number) => void | never } +): void { + let usedDeprecatedArgument = false; + let usedInvalidArgument = false; + + const knownArgs = args as unknown as UserConfig & CliOptions; + // the first position argument should be used + // instead of --connectionString, as it's how the mongosh works. + if (knownArgs.connectionString) { + usedDeprecatedArgument = true; + warn( + "The --connectionString argument is deprecated. Prefer using the MDB_MCP_CONNECTION_STRING environment variable or the first positional argument for the connection string." + ); + } + + for (const providedKey of Object.keys(args)) { + if (providedKey === "_") { + // positional argument + continue; + } + + const { valid, suggestion } = validateConfigKey(providedKey); + if (!valid) { + usedInvalidArgument = true; + if (suggestion) { + warn(`Invalid command line argument '${providedKey}'. Did you mean '${suggestion}'?`); + } else { + warn(`Invalid command line argument '${providedKey}'.`); + } + } + } + + if (usedInvalidArgument || usedDeprecatedArgument) { + warn("Refer to https://www.mongodb.com/docs/mcp-server/get-started/ for setting up the MCP Server."); + } + + if (usedInvalidArgument) { + exit(1); + } +} + function commaSeparatedToArray(str: string | string[] | undefined): T { if (str === undefined) { return [] as unknown as T; @@ -287,6 +414,29 @@ function commaSeparatedToArray(str: string | string[] | unde return str as T; } +export function registerKnownSecretsInRootKeychain(userConfig: Partial): void { + const keychain = Keychain.root; + + const maybeRegister = (value: string | undefined, kind: Secret["kind"]): void => { + if (value) { + keychain.register(value, kind); + } + }; + + maybeRegister(userConfig.apiClientId, "user"); + maybeRegister(userConfig.apiClientSecret, "password"); + maybeRegister(userConfig.awsAccessKeyId, "password"); + maybeRegister(userConfig.awsIamSessionToken, "password"); + maybeRegister(userConfig.awsSecretAccessKey, "password"); + maybeRegister(userConfig.awsSessionToken, "password"); + maybeRegister(userConfig.password, "password"); + maybeRegister(userConfig.tlsCAFile, "url"); + maybeRegister(userConfig.tlsCRLFile, "url"); + maybeRegister(userConfig.tlsCertificateKeyFile, "url"); + maybeRegister(userConfig.tlsCertificateKeyFilePassword, "password"); + maybeRegister(userConfig.username, "user"); +} + export function setupUserConfig({ cli, env, @@ -304,6 +454,7 @@ export function setupUserConfig({ userConfig.disabledTools = commaSeparatedToArray(userConfig.disabledTools); userConfig.loggers = commaSeparatedToArray(userConfig.loggers); + userConfig.confirmationRequiredTools = commaSeparatedToArray(userConfig.confirmationRequiredTools); if (userConfig.connectionString && userConfig.connectionSpecifier) { const connectionInfo = generateConnectionInfoFromCliArgs(userConfig); @@ -340,6 +491,7 @@ export function setupUserConfig({ } } + registerKnownSecretsInRootKeychain(userConfig); return userConfig; } diff --git a/src/common/connectionErrorHandler.ts b/src/common/connectionErrorHandler.ts new file mode 100644 index 000000000..9de63befe --- /dev/null +++ b/src/common/connectionErrorHandler.ts @@ -0,0 +1,82 @@ +import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { ErrorCodes, type MongoDBError } from "./errors.js"; +import type { AnyConnectionState } from "./connectionManager.js"; +import type { ToolBase } from "../tools/tool.js"; + +export type ConnectionErrorHandler = ( + error: MongoDBError, + additionalContext: ConnectionErrorHandlerContext +) => ConnectionErrorUnhandled | ConnectionErrorHandled; + +export type ConnectionErrorHandlerContext = { availableTools: ToolBase[]; connectionState: AnyConnectionState }; +export type ConnectionErrorUnhandled = { errorHandled: false }; +export type ConnectionErrorHandled = { errorHandled: true; result: CallToolResult }; + +export const connectionErrorHandler: ConnectionErrorHandler = (error, { availableTools, connectionState }) => { + const connectTools = availableTools + .filter((t) => t.operationType === "connect") + .sort((a, b) => a.category.localeCompare(b.category)); // Sort Atlas tools before MongoDB tools + + // Find the first Atlas connect tool if available and suggest to the LLM to use it. + // Note: if we ever have multiple Atlas connect tools, we may want to refine this logic to select the most appropriate one. + const atlasConnectTool = connectTools?.find((t) => t.category === "atlas"); + const llmConnectHint = atlasConnectTool + ? `Note to LLM: prefer using the "${atlasConnectTool.name}" tool to connect to an Atlas cluster over using a connection string. Make sure to ask the user to specify a cluster name they want to connect to or ask them if they want to use the "list-clusters" tool to list all their clusters. Do not invent cluster names or connection strings unless the user has explicitly specified them. If they've previously connected to MongoDB using MCP, you can ask them if they want to reconnect using the same cluster/connection.` + : "Note to LLM: do not invent connection strings and explicitly ask the user to provide one. If they have previously connected to MongoDB using MCP, you can ask them if they want to reconnect using the same connection string."; + + const connectToolsNames = connectTools?.map((t) => `"${t.name}"`).join(", "); + const additionalPromptForConnectivity: { type: "text"; text: string }[] = []; + + if (connectionState.tag === "connecting" && connectionState.oidcConnectionType) { + additionalPromptForConnectivity.push({ + type: "text", + text: `The user needs to finish their OIDC connection by opening '${connectionState.oidcLoginUrl}' in the browser and use the following user code: '${connectionState.oidcUserCode}'`, + }); + } else { + additionalPromptForConnectivity.push({ + type: "text", + text: connectToolsNames + ? `Please use one of the following tools: ${connectToolsNames} to connect to a MongoDB instance or update the MCP server configuration to include a connection string. ${llmConnectHint}` + : "There are no tools available to connect. Please update the configuration to include a connection string and restart the server.", + }); + } + + switch (error.code) { + case ErrorCodes.NotConnectedToMongoDB: + return { + errorHandled: true, + result: { + content: [ + { + type: "text", + text: "You need to connect to a MongoDB instance before you can access its data.", + }, + ...additionalPromptForConnectivity, + ], + isError: true, + }, + }; + case ErrorCodes.MisconfiguredConnectionString: + return { + errorHandled: true, + result: { + content: [ + { + type: "text", + text: "The configured connection string is not valid. Please check the connection string and confirm it points to a valid MongoDB instance.", + }, + { + type: "text", + text: connectTools + ? `Alternatively, you can use one of the following tools: ${connectToolsNames} to connect to a MongoDB instance. ${llmConnectHint}` + : "Please update the configuration to use a valid connection string and restart the server.", + }, + ], + isError: true, + }, + }; + + default: + return { errorHandled: false }; + } +}; diff --git a/src/common/connectionManager.ts b/src/common/connectionManager.ts index edb0b56ea..ba7f4cb47 100644 --- a/src/common/connectionManager.ts +++ b/src/common/connectionManager.ts @@ -1,17 +1,14 @@ -import type { UserConfig, DriverOptions } from "./config.js"; -import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; -import EventEmitter from "events"; -import { setAppNameParamIfMissing } from "../helpers/connectionOptions.js"; -import { packageInfo } from "./packageInfo.js"; -import ConnectionString from "mongodb-connection-string-url"; +import { EventEmitter } from "events"; import type { MongoClientOptions } from "mongodb"; -import { ErrorCodes, MongoDBError } from "./errors.js"; +import { ConnectionString } from "mongodb-connection-string-url"; +import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; +import { type ConnectionInfo, generateConnectionInfoFromCliArgs } from "@mongosh/arg-parser"; import type { DeviceId } from "../helpers/deviceId.js"; -import type { AppNameComponents } from "../helpers/connectionOptions.js"; -import type { CompositeLogger } from "./logger.js"; -import { LogId } from "./logger.js"; -import type { ConnectionInfo } from "@mongosh/arg-parser"; -import { generateConnectionInfoFromCliArgs } from "@mongosh/arg-parser"; +import { defaultDriverOptions, setupDriverConfig, type DriverOptions, type UserConfig } from "./config.js"; +import { MongoDBError, ErrorCodes } from "./errors.js"; +import { type LoggerBase, LogId } from "./logger.js"; +import { packageInfo } from "./packageInfo.js"; +import { type AppNameComponents, setAppNameParamIfMissing } from "../helpers/connectionOptions.js"; export interface AtlasClusterConnectionInfo { username: string; @@ -71,39 +68,76 @@ export interface ConnectionManagerEvents { "connection-error": [ConnectionStateErrored]; } -export class ConnectionManager extends EventEmitter { +/** + * For a few tests, we need the changeState method to force a connection state + * which is we have this type to typecast the actual ConnectionManager with + * public changeState (only to make TS happy). + */ +export type TestConnectionManager = ConnectionManager & { + changeState( + event: Event, + newState: State + ): State; +}; + +export abstract class ConnectionManager { + protected clientName: string; + protected readonly _events: EventEmitter; + readonly events: Pick, "on" | "off" | "once">; private state: AnyConnectionState; + + constructor() { + this.clientName = "unknown"; + this.events = this._events = new EventEmitter(); + this.state = { tag: "disconnected" }; + } + + get currentConnectionState(): AnyConnectionState { + return this.state; + } + + protected changeState( + event: Event, + newState: State + ): State { + this.state = newState; + // TypeScript doesn't seem to be happy with the spread operator and generics + // eslint-disable-next-line + this._events.emit(event, ...([newState] as any)); + return newState; + } + + setClientName(clientName: string): void { + this.clientName = clientName; + } + + abstract connect(settings: ConnectionSettings): Promise; + + abstract disconnect(): Promise; +} + +export class MCPConnectionManager extends ConnectionManager { private deviceId: DeviceId; - private clientName: string; private bus: EventEmitter; constructor( private userConfig: UserConfig, private driverOptions: DriverOptions, - private logger: CompositeLogger, + private logger: LoggerBase, deviceId: DeviceId, bus?: EventEmitter ) { super(); - this.bus = bus ?? new EventEmitter(); - this.state = { tag: "disconnected" }; - this.bus.on("mongodb-oidc-plugin:auth-failed", this.onOidcAuthFailed.bind(this)); this.bus.on("mongodb-oidc-plugin:auth-succeeded", this.onOidcAuthSucceeded.bind(this)); - this.deviceId = deviceId; - this.clientName = "unknown"; - } - - setClientName(clientName: string): void { - this.clientName = clientName; } async connect(settings: ConnectionSettings): Promise { - this.emit("connection-request", this.state); + this._events.emit("connection-request", this.currentConnectionState); - if (this.state.tag === "connected" || this.state.tag === "connecting") { + if (this.currentConnectionState.tag === "connected" || this.currentConnectionState.tag === "connecting") { await this.disconnect(); } @@ -138,7 +172,7 @@ export class ConnectionManager extends EventEmitter { connectionInfo.driverOptions.proxy ??= { useEnvironmentVariableProxies: true }; connectionInfo.driverOptions.applyProxyToOIDC ??= true; - connectionStringAuthType = ConnectionManager.inferConnectionTypeFromSettings( + connectionStringAuthType = MCPConnectionManager.inferConnectionTypeFromSettings( this.userConfig, connectionInfo ); @@ -165,16 +199,15 @@ export class ConnectionManager extends EventEmitter { } try { - const connectionType = ConnectionManager.inferConnectionTypeFromSettings(this.userConfig, connectionInfo); - if (connectionType.startsWith("oidc")) { + if (connectionStringAuthType.startsWith("oidc")) { void this.pingAndForget(serviceProvider); return this.changeState("connection-request", { tag: "connecting", connectedAtlasCluster: settings.atlas, serviceProvider, - connectionStringAuthType: connectionType, - oidcConnectionType: connectionType as OIDCConnectionAuthType, + connectionStringAuthType, + oidcConnectionType: connectionStringAuthType as OIDCConnectionAuthType, }); } @@ -184,7 +217,7 @@ export class ConnectionManager extends EventEmitter { tag: "connected", connectedAtlasCluster: settings.atlas, serviceProvider, - connectionStringAuthType: connectionType, + connectionStringAuthType, }); } catch (error: unknown) { const errorReason = error instanceof Error ? error.message : `${error as string}`; @@ -199,13 +232,13 @@ export class ConnectionManager extends EventEmitter { } async disconnect(): Promise { - if (this.state.tag === "disconnected" || this.state.tag === "errored") { - return this.state; + if (this.currentConnectionState.tag === "disconnected" || this.currentConnectionState.tag === "errored") { + return this.currentConnectionState; } - if (this.state.tag === "connected" || this.state.tag === "connecting") { + if (this.currentConnectionState.tag === "connected" || this.currentConnectionState.tag === "connecting") { try { - await this.state.serviceProvider?.close(true); + await this.currentConnectionState.serviceProvider?.close(true); } finally { this.changeState("connection-close", { tag: "disconnected", @@ -216,30 +249,21 @@ export class ConnectionManager extends EventEmitter { return { tag: "disconnected" }; } - get currentConnectionState(): AnyConnectionState { - return this.state; - } - - changeState( - event: Event, - newState: State - ): State { - this.state = newState; - // TypeScript doesn't seem to be happy with the spread operator and generics - // eslint-disable-next-line - this.emit(event, ...([newState] as any)); - return newState; - } - private onOidcAuthFailed(error: unknown): void { - if (this.state.tag === "connecting" && this.state.connectionStringAuthType?.startsWith("oidc")) { + if ( + this.currentConnectionState.tag === "connecting" && + this.currentConnectionState.connectionStringAuthType?.startsWith("oidc") + ) { void this.disconnectOnOidcError(error); } } private onOidcAuthSucceeded(): void { - if (this.state.tag === "connecting" && this.state.connectionStringAuthType?.startsWith("oidc")) { - this.changeState("connection-success", { ...this.state, tag: "connected" }); + if ( + this.currentConnectionState.tag === "connecting" && + this.currentConnectionState.connectionStringAuthType?.startsWith("oidc") + ) { + this.changeState("connection-success", { ...this.currentConnectionState, tag: "connected" }); } this.logger.info({ @@ -250,9 +274,12 @@ export class ConnectionManager extends EventEmitter { } private onOidcNotifyDeviceFlow(flowInfo: { verificationUrl: string; userCode: string }): void { - if (this.state.tag === "connecting" && this.state.connectionStringAuthType?.startsWith("oidc")) { + if ( + this.currentConnectionState.tag === "connecting" && + this.currentConnectionState.connectionStringAuthType?.startsWith("oidc") + ) { this.changeState("connection-request", { - ...this.state, + ...this.currentConnectionState, tag: "connecting", connectionStringAuthType: "oidc-device-flow", oidcLoginUrl: flowInfo.verificationUrl, @@ -329,3 +356,23 @@ export class ConnectionManager extends EventEmitter { } } } + +/** + * Consumers of MCP server library have option to bring their own connection + * management if they need to. To support that, we enable injecting connection + * manager implementation through a factory function. + */ +export type ConnectionManagerFactoryFn = (createParams: { + logger: LoggerBase; + deviceId: DeviceId; + userConfig: UserConfig; +}) => Promise; + +export const createMCPConnectionManager: ConnectionManagerFactoryFn = ({ logger, deviceId, userConfig }) => { + const driverOptions = setupDriverConfig({ + config: userConfig, + defaults: defaultDriverOptions, + }); + + return Promise.resolve(new MCPConnectionManager(userConfig, driverOptions, logger, deviceId)); +}; diff --git a/src/common/errors.ts b/src/common/errors.ts index d81867f09..1ef987de4 100644 --- a/src/common/errors.ts +++ b/src/common/errors.ts @@ -2,11 +2,12 @@ export enum ErrorCodes { NotConnectedToMongoDB = 1_000_000, MisconfiguredConnectionString = 1_000_001, ForbiddenCollscan = 1_000_002, + ForbiddenWriteOperation = 1_000_003, } -export class MongoDBError extends Error { +export class MongoDBError extends Error { constructor( - public code: ErrorCodes, + public code: ErrorCode, message: string ) { super(message); diff --git a/src/common/exportsManager.ts b/src/common/exportsManager.ts index 86b7cc57b..f8ce94501 100644 --- a/src/common/exportsManager.ts +++ b/src/common/exportsManager.ts @@ -27,6 +27,7 @@ interface CommonExportData { interface ReadyExport extends CommonExportData { exportStatus: "ready"; exportCreatedAt: number; + docsTransformed: number; } interface InProgressExport extends CommonExportData { @@ -56,7 +57,7 @@ type StoredExport = ReadyExport | InProgressExport; * * Ref Cursor: https://forum.cursor.com/t/cursor-mcp-resource-feature-support/50987 * JIRA: https://jira.mongodb.org/browse/MCP-104 */ -type AvailableExport = Pick; +export type AvailableExport = Pick; export type ExportsManagerConfig = Pick; @@ -124,10 +125,10 @@ export class ExportsManager extends EventEmitter { } } - public async readExport(exportName: string): Promise { + public async readExport(exportName: string): Promise<{ content: string; docsTransformed: number }> { try { this.assertIsNotShuttingDown(); - exportName = decodeURIComponent(exportName); + exportName = decodeAndNormalize(exportName); const exportHandle = this.storedExports[exportName]; if (!exportHandle) { throw new Error("Requested export has either expired or does not exist."); @@ -137,9 +138,12 @@ export class ExportsManager extends EventEmitter { throw new Error("Requested export is still being generated. Try again later."); } - const { exportPath } = exportHandle; + const { exportPath, docsTransformed } = exportHandle; - return fs.readFile(exportPath, { encoding: "utf8", signal: this.shutdownController.signal }); + return { + content: await fs.readFile(exportPath, { encoding: "utf8", signal: this.shutdownController.signal }), + docsTransformed, + }; } catch (error) { this.logger.error({ id: LogId.exportReadError, @@ -163,7 +167,7 @@ export class ExportsManager extends EventEmitter { }): Promise { try { this.assertIsNotShuttingDown(); - const exportNameWithExtension = validateExportName(ensureExtension(exportName, "json")); + const exportNameWithExtension = decodeAndNormalize(ensureExtension(exportName, "json")); if (this.storedExports[exportNameWithExtension]) { return Promise.reject( new Error("Export with same name is either already available or being generated.") @@ -202,17 +206,15 @@ export class ExportsManager extends EventEmitter { }): Promise { try { let pipeSuccessful = false; + let docsTransformed = 0; try { await fs.mkdir(this.exportsDirectoryPath, { recursive: true }); const outputStream = createWriteStream(inProgressExport.exportPath); - await pipeline( - [ - input.stream(), - this.docToEJSONStream(this.getEJSONOptionsForFormat(jsonExportFormat)), - outputStream, - ], - { signal: this.shutdownController.signal } - ); + const ejsonTransform = this.docToEJSONStream(this.getEJSONOptionsForFormat(jsonExportFormat)); + await pipeline([input.stream(), ejsonTransform, outputStream], { + signal: this.shutdownController.signal, + }); + docsTransformed = ejsonTransform.docsTransformed; pipeSuccessful = true; } catch (error) { // If the pipeline errors out then we might end up with @@ -231,6 +233,7 @@ export class ExportsManager extends EventEmitter { ...inProgressExport, exportCreatedAt: Date.now(), exportStatus: "ready", + docsTransformed, }; this.emit("export-available", inProgressExport.exportURI); } @@ -256,33 +259,39 @@ export class ExportsManager extends EventEmitter { } } - private docToEJSONStream(ejsonOptions: EJSONOptions | undefined): Transform { + private docToEJSONStream(ejsonOptions: EJSONOptions | undefined): Transform & { docsTransformed: number } { let docsTransformed = 0; - return new Transform({ - objectMode: true, - transform(chunk: unknown, encoding, callback): void { - try { - const doc = EJSON.stringify(chunk, undefined, undefined, ejsonOptions); + const result = Object.assign( + new Transform({ + objectMode: true, + transform(chunk: unknown, encoding, callback): void { + try { + const doc = EJSON.stringify(chunk, undefined, undefined, ejsonOptions); + if (docsTransformed === 0) { + this.push("[" + doc); + } else { + this.push(",\n" + doc); + } + docsTransformed++; + callback(); + } catch (err) { + callback(err as Error); + } + }, + flush(callback): void { if (docsTransformed === 0) { - this.push("[" + doc); + this.push("[]"); } else { - this.push(",\n" + doc); + this.push("]"); } - docsTransformed++; + result.docsTransformed = docsTransformed; callback(); - } catch (err) { - callback(err as Error); - } - }, - flush(callback): void { - if (docsTransformed === 0) { - this.push("[]"); - } else { - this.push("]"); - } - callback(); - }, - }); + }, + }), + { docsTransformed } + ); + + return result; } private async cleanupExpiredExports(): Promise { @@ -363,6 +372,10 @@ export class ExportsManager extends EventEmitter { } } +export function decodeAndNormalize(text: string): string { + return decodeURIComponent(text).normalize("NFKC"); +} + /** * Ensures the path ends with the provided extension */ export function ensureExtension(pathOrName: string, extension: string): string { @@ -373,22 +386,6 @@ export function ensureExtension(pathOrName: string, extension: string): string { return `${pathOrName}${extWithDot}`; } -/** - * Small utility to decoding and validating provided export name for path - * traversal or no extension */ -export function validateExportName(nameWithExtension: string): string { - const decodedName = decodeURIComponent(nameWithExtension); - if (!path.extname(decodedName)) { - throw new Error("Provided export name has no extension"); - } - - if (decodedName.includes("..") || decodedName.includes("/") || decodedName.includes("\\")) { - throw new Error("Invalid export name: path traversal hinted"); - } - - return decodedName; -} - export function isExportExpired(createdAt: number, exportTimeoutMs: number): boolean { return Date.now() - createdAt > exportTimeoutMs; } diff --git a/src/common/keychain.ts b/src/common/keychain.ts new file mode 100644 index 000000000..3e4e180aa --- /dev/null +++ b/src/common/keychain.ts @@ -0,0 +1,40 @@ +import type { Secret } from "mongodb-redact"; +export type { Secret } from "mongodb-redact"; + +/** + * This class holds the secrets of a single server. Ideally, we might want to have a keychain + * per session, but right now the loggers are set up by server and are not aware of the concept + * of session and this would require a bigger refactor. + * + * Whenever we identify or create a secret (for example, Atlas login, CLI arguments...) we + * should register them in the root Keychain (`Keychain.root.register`) or preferably + * on the session keychain if available `this.session.keychain`. + **/ +export class Keychain { + private secrets: Secret[]; + private static rootKeychain: Keychain = new Keychain(); + + constructor() { + this.secrets = []; + } + + static get root(): Keychain { + return Keychain.rootKeychain; + } + + register(value: Secret["value"], kind: Secret["kind"]): void { + this.secrets.push({ value, kind }); + } + + clearAllSecrets(): void { + this.secrets = []; + } + + get allSecrets(): Secret[] { + return [...this.secrets]; + } +} + +export function registerGlobalSecretToRedact(value: Secret["value"], kind: Secret["kind"]): void { + Keychain.root.register(value, kind); +} diff --git a/src/common/logger.ts b/src/common/logger.ts index 40b54cc66..7a3ebd99c 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -1,10 +1,11 @@ import fs from "fs/promises"; import type { MongoLogId, MongoLogWriter } from "mongodb-log-writer"; import { mongoLogId, MongoLogManager } from "mongodb-log-writer"; -import redact from "mongodb-redact"; +import { redact } from "mongodb-redact"; import type { LoggingMessageNotification } from "@modelcontextprotocol/sdk/types.js"; import { EventEmitter } from "events"; import type { Server } from "../lib.js"; +import type { Keychain } from "./keychain.js"; export type LogLevel = LoggingMessageNotification["params"]["level"]; @@ -34,6 +35,7 @@ export const LogId = { telemetryMetadataError: mongoLogId(1_002_005), deviceIdResolutionError: mongoLogId(1_002_006), deviceIdTimeout: mongoLogId(1_002_007), + telemetryClose: mongoLogId(1_002_008), toolExecute: mongoLogId(1_003_001), toolExecuteFailure: mongoLogId(1_003_002), @@ -41,6 +43,7 @@ export const LogId = { mongodbConnectFailure: mongoLogId(1_004_001), mongodbDisconnectFailure: mongoLogId(1_004_002), + mongodbConnectTry: mongoLogId(1_004_003), toolUpdateFailure: mongoLogId(1_005_001), resourceUpdateFailure: mongoLogId(1_005_002), @@ -53,6 +56,7 @@ export const LogId = { streamableHttpTransportCloseFailure: mongoLogId(1_006_006), streamableHttpTransportKeepAliveFailure: mongoLogId(1_006_007), streamableHttpTransportKeepAlive: mongoLogId(1_006_008), + streamableHttpTransportHttpHostWarning: mongoLogId(1_006_009), exportCleanupError: mongoLogId(1_007_001), exportCreationError: mongoLogId(1_007_002), @@ -83,6 +87,10 @@ type DefaultEventMap = [never]; export abstract class LoggerBase = DefaultEventMap> extends EventEmitter { private readonly defaultUnredactedLogger: LoggerType = "mcp"; + constructor(private readonly keychain: Keychain | undefined) { + super(); + } + public log(level: LogLevel, payload: LogPayload): void { // If no explicit value is supplied for unredacted loggers, default to "mcp" const noRedaction = payload.noRedaction !== undefined ? payload.noRedaction : this.defaultUnredactedLogger; @@ -121,7 +129,7 @@ export abstract class LoggerBase = DefaultEventMap> extend return message; } - return redact(message); + return redact(message, this.keychain?.allSecrets ?? []); } public info(payload: LogPayload): void { @@ -179,6 +187,10 @@ export abstract class LoggerBase = DefaultEventMap> extend export class ConsoleLogger extends LoggerBase { protected readonly type: LoggerType = "console"; + public constructor(keychain: Keychain) { + super(keychain); + } + protected logCore(level: LogLevel, payload: LogPayload): void { const { id, context, message } = payload; console.error( @@ -200,8 +212,8 @@ export class DiskLogger extends LoggerBase<{ initialized: [] }> { private bufferedMessages: { level: LogLevel; payload: LogPayload }[] = []; private logWriter?: MongoLogWriter; - public constructor(logPath: string, onError: (error: Error) => void) { - super(); + public constructor(logPath: string, onError: (error: Error) => void, keychain: Keychain) { + super(keychain); void this.initialize(logPath, onError); } @@ -250,7 +262,7 @@ export class DiskLogger extends LoggerBase<{ initialized: [] }> { } export class McpLogger extends LoggerBase { - private static readonly LOG_LEVELS: LogLevel[] = [ + public static readonly LOG_LEVELS: LogLevel[] = [ "debug", "info", "notice", @@ -259,10 +271,13 @@ export class McpLogger extends LoggerBase { "critical", "alert", "emergency", - ]; + ] as const; - public constructor(private readonly server: Server) { - super(); + public constructor( + private readonly server: Server, + keychain: Keychain + ) { + super(keychain); } protected readonly type: LoggerType = "mcp"; @@ -294,7 +309,9 @@ export class CompositeLogger extends LoggerBase { private readonly attributes: Record = {}; constructor(...loggers: LoggerBase[]) { - super(); + // composite logger does not redact, only the actual delegates do the work + // so we don't need the Keychain here + super(undefined); this.loggers = loggers; } @@ -326,6 +343,10 @@ export class CompositeLogger extends LoggerBase { export class NullLogger extends LoggerBase { protected type?: LoggerType; + constructor() { + super(undefined); + } + protected logCore(): void { // No-op logger, does not log anything } diff --git a/src/common/packageInfo.ts b/src/common/packageInfo.ts index 680859f2e..341558a96 100644 --- a/src/common/packageInfo.ts +++ b/src/common/packageInfo.ts @@ -1,5 +1,5 @@ // This file was generated by scripts/updatePackageVersion.ts - Do not edit it manually. export const packageInfo = { - version: "0.3.0", + version: "1.0.0", mcpServerName: "MongoDB MCP Server", }; diff --git a/src/common/session.ts b/src/common/session.ts index 87113894c..24946b171 100644 --- a/src/common/session.ts +++ b/src/common/session.ts @@ -15,6 +15,7 @@ import type { import type { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; import { ErrorCodes, MongoDBError } from "./errors.js"; import type { ExportsManager } from "./exportsManager.js"; +import type { Keychain } from "./keychain.js"; export interface SessionOptions { apiBaseUrl: string; @@ -23,6 +24,7 @@ export interface SessionOptions { logger: CompositeLogger; exportsManager: ExportsManager; connectionManager: ConnectionManager; + keychain: Keychain; } export type SessionEvents = { @@ -37,6 +39,8 @@ export class Session extends EventEmitter { readonly exportsManager: ExportsManager; readonly connectionManager: ConnectionManager; readonly apiClient: ApiClient; + readonly keychain: Keychain; + mcpClient?: { name?: string; version?: string; @@ -52,9 +56,11 @@ export class Session extends EventEmitter { logger, connectionManager, exportsManager, + keychain, }: SessionOptions) { super(); + this.keychain = keychain; this.logger = logger; const credentials: ApiClientCredentials | undefined = apiClientId && apiClientSecret @@ -67,10 +73,10 @@ export class Session extends EventEmitter { this.apiClient = new ApiClient({ baseUrl: apiBaseUrl, credentials }, logger); this.exportsManager = exportsManager; this.connectionManager = connectionManager; - this.connectionManager.on("connection-success", () => this.emit("connect")); - this.connectionManager.on("connection-time-out", (error) => this.emit("connection-error", error)); - this.connectionManager.on("connection-close", () => this.emit("disconnect")); - this.connectionManager.on("connection-error", (error) => this.emit("connection-error", error)); + this.connectionManager.events.on("connection-success", () => this.emit("connect")); + this.connectionManager.events.on("connection-time-out", (error) => this.emit("connection-error", error)); + this.connectionManager.events.on("connection-close", () => this.emit("disconnect")); + this.connectionManager.events.on("connection-error", (error) => this.emit("connection-error", error)); } setMcpClient(mcpClient: Implementation | undefined): void { diff --git a/src/elicitation.ts b/src/elicitation.ts new file mode 100644 index 000000000..c3d30d5b9 --- /dev/null +++ b/src/elicitation.ts @@ -0,0 +1,53 @@ +import type { ElicitRequest } from "@modelcontextprotocol/sdk/types.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; + +export class Elicitation { + private readonly server: McpServer["server"]; + constructor({ server }: { server: McpServer["server"] }) { + this.server = server; + } + + /** + * Checks if the client supports elicitation capabilities. + * @returns True if the client supports elicitation, false otherwise. + */ + public supportsElicitation(): boolean { + const clientCapabilities = this.server.getClientCapabilities(); + return clientCapabilities?.elicitation !== undefined; + } + + /** + * Requests a boolean confirmation from the user. + * @param message - The message to display to the user. + * @returns True if the user confirms the action or the client does not support elicitation, false otherwise. + */ + public async requestConfirmation(message: string): Promise { + if (!this.supportsElicitation()) { + return true; + } + + const result = await this.server.elicitInput({ + message, + requestedSchema: Elicitation.CONFIRMATION_SCHEMA, + }); + return result.action === "accept" && result.content?.confirmation === "Yes"; + } + + /** + * The schema for the confirmation question. + * TODO: In the future would be good to use Zod 4's toJSONSchema() to generate the schema. + */ + public static CONFIRMATION_SCHEMA: ElicitRequest["params"]["requestedSchema"] = { + type: "object", + properties: { + confirmation: { + type: "string", + title: "Would you like to confirm?", + description: "Would you like to confirm?", + enum: ["Yes", "No"], + enumNames: ["Yes, I confirm", "No, I do not confirm"], + }, + }, + required: ["confirmation"], + }; +} diff --git a/src/helpers/connectionOptions.ts b/src/helpers/connectionOptions.ts index f0e86fe2e..4a63531d8 100644 --- a/src/helpers/connectionOptions.ts +++ b/src/helpers/connectionOptions.ts @@ -1,5 +1,5 @@ import type { MongoClientOptions } from "mongodb"; -import ConnectionString from "mongodb-connection-string-url"; +import { ConnectionString } from "mongodb-connection-string-url"; export interface AppNameComponents { appName: string; diff --git a/src/index.ts b/src/index.ts index b1ac4b489..c4013b4e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,12 +36,13 @@ function enableFipsIfRequested(): void { enableFipsIfRequested(); import { ConsoleLogger, LogId } from "./common/logger.js"; -import { config, driverOptions } from "./common/config.js"; +import { config } from "./common/config.js"; import crypto from "crypto"; import { packageInfo } from "./common/packageInfo.js"; import { StdioRunner } from "./transports/stdio.js"; import { StreamableHttpRunner } from "./transports/streamableHttp.js"; import { systemCA } from "@mongodb-js/devtools-proxy-support"; +import { Keychain } from "./common/keychain.js"; async function main(): Promise { systemCA().catch(() => undefined); // load system CA asynchronously as in mongosh @@ -51,8 +52,12 @@ async function main(): Promise { const transportRunner = config.transport === "stdio" - ? new StdioRunner(config, driverOptions) - : new StreamableHttpRunner(config, driverOptions); + ? new StdioRunner({ + userConfig: config, + }) + : new StreamableHttpRunner({ + userConfig: config, + }); const shutdown = (): void => { transportRunner.logger.info({ id: LogId.serverCloseRequested, @@ -117,7 +122,7 @@ main().catch((error: unknown) => { // At this point, we may be in a very broken state, so we can't rely on the logger // being functional. Instead, create a brand new ConsoleLogger and log the error // to the console. - const logger = new ConsoleLogger(); + const logger = new ConsoleLogger(Keychain.root); logger.emergency({ id: LogId.serverStartFailure, context: "server", diff --git a/src/lib.ts b/src/lib.ts index 9fd921e4c..c81472e0b 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -1,7 +1,23 @@ export { Server, type ServerOptions } from "./server.js"; -export { Telemetry } from "./telemetry/telemetry.js"; export { Session, type SessionOptions } from "./common/session.js"; -export { type UserConfig, defaultUserConfig } from "./common/config.js"; +export { defaultUserConfig, type UserConfig, ALL_CONFIG_KEYS as configurableProperties } from "./common/config.js"; +export { LoggerBase, type LogPayload, type LoggerType, type LogLevel } from "./common/logger.js"; export { StreamableHttpRunner } from "./transports/streamableHttp.js"; -export { LoggerBase } from "./common/logger.js"; -export type { LogPayload, LoggerType, LogLevel } from "./common/logger.js"; +export { + ConnectionManager, + type AnyConnectionState, + type ConnectionState, + type ConnectionStateDisconnected, + type ConnectionStateErrored, + type ConnectionManagerFactoryFn, +} from "./common/connectionManager.js"; +export type { + ConnectionErrorHandler, + ConnectionErrorHandled, + ConnectionErrorUnhandled, + ConnectionErrorHandlerContext, +} from "./common/connectionErrorHandler.js"; +export { ErrorCodes } from "./common/errors.js"; +export { Telemetry } from "./telemetry/telemetry.js"; +export { Keychain, registerGlobalSecretToRedact } from "./common/keychain.js"; +export type { Secret } from "./common/keychain.js"; diff --git a/src/resources/common/exportedData.ts b/src/resources/common/exportedData.ts index b1b5ed2cf..7fed7dbab 100644 --- a/src/resources/common/exportedData.ts +++ b/src/resources/common/exportedData.ts @@ -7,6 +7,7 @@ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import type { Server } from "../../server.js"; import { LogId } from "../../common/logger.js"; import type { Session } from "../../common/session.js"; +import { formatUntrustedData } from "../../tools/tool.js"; export class ExportedData { private readonly name = "exported-data"; @@ -72,7 +73,12 @@ export class ExportedData { private autoCompleteExportName: CompleteResourceTemplateCallback = (value) => { try { return this.session.exportsManager.availableExports - .filter(({ exportName }) => exportName.startsWith(value)) + .filter(({ exportName, exportTitle }) => { + const lcExportName = exportName.toLowerCase(); + const lcExportTitle = exportTitle.toLowerCase(); + const lcValue = value.toLowerCase(); + return lcExportName.startsWith(lcValue) || lcExportTitle.includes(lcValue); + }) .map(({ exportName }) => exportName); } catch (error) { this.session.logger.error({ @@ -90,13 +96,17 @@ export class ExportedData { throw new Error("Cannot retrieve exported data, exportName not provided."); } - const content = await this.session.exportsManager.readExport(exportName); + const { content, docsTransformed } = await this.session.exportsManager.readExport(exportName); + + const text = formatUntrustedData(`The exported data contains ${docsTransformed} documents.`, content) + .map((t) => t.text) + .join("\n"); return { contents: [ { uri: url.href, - text: content, + text, mimeType: "application/json", }, ], diff --git a/src/server.ts b/src/server.ts index 06a216948..2e6ac2c46 100644 --- a/src/server.ts +++ b/src/server.ts @@ -5,7 +5,7 @@ import { AtlasTools } from "./tools/atlas/tools.js"; import { MongoDbTools } from "./tools/mongodb/tools.js"; import { Resources } from "./resources/resources.js"; import type { LogLevel } from "./common/logger.js"; -import { LogId } from "./common/logger.js"; +import { LogId, McpLogger } from "./common/logger.js"; import type { Telemetry } from "./telemetry/telemetry.js"; import type { UserConfig } from "./common/config.js"; import { type ServerEvent } from "./telemetry/types.js"; @@ -20,12 +20,17 @@ import { import assert from "assert"; import type { ToolBase } from "./tools/tool.js"; import { validateConnectionString } from "./helpers/connectionOptions.js"; +import { packageInfo } from "./common/packageInfo.js"; +import { type ConnectionErrorHandler } from "./common/connectionErrorHandler.js"; +import type { Elicitation } from "./elicitation.js"; export interface ServerOptions { session: Session; userConfig: UserConfig; mcpServer: McpServer; telemetry: Telemetry; + elicitation: Elicitation; + connectionErrorHandler: ConnectionErrorHandler; } export class Server { @@ -33,7 +38,9 @@ export class Server { public readonly mcpServer: McpServer; private readonly telemetry: Telemetry; public readonly userConfig: UserConfig; + public readonly elicitation: Elicitation; public readonly tools: ToolBase[] = []; + public readonly connectionErrorHandler: ConnectionErrorHandler; private _mcpLogLevel: LogLevel = "debug"; @@ -44,12 +51,14 @@ export class Server { private readonly startTime: number; private readonly subscriptions = new Set(); - constructor({ session, mcpServer, userConfig, telemetry }: ServerOptions) { + constructor({ session, mcpServer, userConfig, telemetry, connectionErrorHandler, elicitation }: ServerOptions) { this.startTime = Date.now(); this.session = session; this.telemetry = telemetry; this.mcpServer = mcpServer; this.userConfig = userConfig; + this.elicitation = elicitation; + this.connectionErrorHandler = connectionErrorHandler; } async connect(transport: Transport): Promise { @@ -107,6 +116,10 @@ export class Server { }); this.mcpServer.server.setRequestHandler(SetLevelRequestSchema, ({ params }) => { + if (!McpLogger.LOG_LEVELS.includes(params.level)) { + throw new Error(`Invalid log level: ${params.level}`); + } + this._mcpLogLevel = params.level; return {}; }); @@ -115,11 +128,10 @@ export class Server { this.session.setMcpClient(this.mcpServer.server.getClientVersion()); // Placed here to start the connection to the config connection string as soon as the server is initialized. void this.connectToConfigConnectionString(); - this.session.logger.info({ id: LogId.serverInitialized, context: "server", - message: `Server started with transport ${transport.constructor.name} and agent runner ${this.session.mcpClient?.name}`, + message: `Server with version ${packageInfo.version} started with transport ${transport.constructor.name} and agent runner ${JSON.stringify(this.session.mcpClient)}`, }); this.emitServerEvent("start", Date.now() - this.startTime); @@ -176,6 +188,7 @@ export class Server { event.properties.startup_time_ms = commandDuration; event.properties.read_only_mode = this.userConfig.readOnly || false; event.properties.disabled_tools = this.userConfig.disabledTools || []; + event.properties.confirmation_required_tools = this.userConfig.confirmationRequiredTools || []; } if (command === "stop") { event.properties.runtime_duration_ms = Date.now() - this.startTime; @@ -185,12 +198,17 @@ export class Server { } } - this.telemetry.emitEvents([event]).catch(() => {}); + this.telemetry.emitEvents([event]); } private registerTools(): void { for (const toolConstructor of [...AtlasTools, ...MongoDbTools]) { - const tool = new toolConstructor(this.session, this.userConfig, this.telemetry); + const tool = new toolConstructor({ + session: this.session, + config: this.userConfig, + telemetry: this.telemetry, + elicitation: this.elicitation, + }); if (tool.register(this)) { this.tools.push(tool); } @@ -240,15 +258,21 @@ export class Server { private async connectToConfigConnectionString(): Promise { if (this.userConfig.connectionString) { try { + this.session.logger.info({ + id: LogId.mongodbConnectTry, + context: "server", + message: `Detected a MongoDB connection string in the configuration, trying to connect...`, + }); await this.session.connectToMongoDB({ connectionString: this.userConfig.connectionString, }); } catch (error) { - console.error( - "Failed to connect to MongoDB instance using the connection string from the config: ", - error - ); - throw new Error("Failed to connect to MongoDB instance using the connection string from the config"); + // We don't throw an error here because we want to allow the server to start even if the connection string is invalid. + this.session.logger.error({ + id: LogId.mongodbConnectFailure, + context: "server", + message: `Failed to connect to MongoDB instance using the connection string from the config: ${error instanceof Error ? error.message : String(error)}`, + }); } } } diff --git a/src/telemetry/eventCache.ts b/src/telemetry/eventCache.ts index c75547661..c6411bf54 100644 --- a/src/telemetry/eventCache.ts +++ b/src/telemetry/eventCache.ts @@ -34,11 +34,18 @@ export class EventCache { } /** - * Gets a copy of the currently cached events + * Gets the number of currently cached events + */ + public get size(): number { + return this.cache.size; + } + + /** + * Gets a copy of the currently cached events along with their ids * @returns Array of cached BaseEvent objects */ - public getEvents(): BaseEvent[] { - return Array.from(this.cache.values()); + public getEvents(): { id: number; event: BaseEvent }[] { + return Array.from(this.cache.entries()).map(([id, event]) => ({ id, event })); } /** @@ -53,10 +60,11 @@ export class EventCache { } /** - * Clears all cached events + * Removes cached events by their ids */ - public clearEvents(): void { - this.cache.clear(); - this.nextId = 0; + public removeEvents(ids: number[]): void { + for (const id of ids) { + this.cache.delete(id); + } } } diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index c820ec614..bdba51a51 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -7,16 +7,25 @@ import { MACHINE_METADATA } from "./constants.js"; import { EventCache } from "./eventCache.js"; import { detectContainerEnv } from "../helpers/container.js"; import type { DeviceId } from "../helpers/deviceId.js"; +import { EventEmitter } from "events"; type EventResult = { success: boolean; error?: Error; }; +export interface TelemetryEvents { + "events-emitted": []; + "events-send-failed": []; + "events-skipped": []; +} + export class Telemetry { private isBufferingEvents: boolean = true; /** Resolves when the setup is complete or a timeout occurs */ public setupPromise: Promise<[string, boolean]> | undefined; + public readonly events: EventEmitter = new EventEmitter(); + private eventCache: EventCache; private deviceId: DeviceId; @@ -35,14 +44,21 @@ export class Telemetry { userConfig: UserConfig, deviceId: DeviceId, { - commonProperties = { ...MACHINE_METADATA }, + commonProperties = {}, eventCache = EventCache.getInstance(), }: { + commonProperties?: Partial; eventCache?: EventCache; - commonProperties?: CommonProperties; } = {} ): Telemetry { - const instance = new Telemetry(session, userConfig, commonProperties, { eventCache, deviceId }); + const mergedProperties = { + ...MACHINE_METADATA, + ...commonProperties, + }; + const instance = new Telemetry(session, userConfig, mergedProperties, { + eventCache, + deviceId, + }); void instance.setup(); return instance; @@ -50,6 +66,12 @@ export class Telemetry { private async setup(): Promise { if (!this.isTelemetryEnabled()) { + this.session.logger.info({ + id: LogId.telemetryEmitFailure, + context: "telemetry", + message: "Telemetry is disabled.", + noRedaction: true, + }); return; } @@ -64,34 +86,47 @@ export class Telemetry { public async close(): Promise { this.isBufferingEvents = false; - await this.emitEvents(this.eventCache.getEvents()); + + this.session.logger.debug({ + id: LogId.telemetryClose, + message: `Closing telemetry and flushing ${this.eventCache.size} events`, + context: "telemetry", + }); + + // Wait up to 5 seconds for events to be sent before closing, but don't throw if it times out + const flushMaxWaitTime = 5000; + let flushTimeout: NodeJS.Timeout | undefined; + await Promise.race([ + new Promise((resolve) => { + flushTimeout = setTimeout(() => { + this.session.logger.debug({ + id: LogId.telemetryClose, + message: `Failed to flush remaining events within ${flushMaxWaitTime}ms timeout`, + context: "telemetry", + }); + resolve(); + }, flushMaxWaitTime); + flushTimeout.unref(); + }), + this.emit([]), + ]); + + clearTimeout(flushTimeout); } /** * Emits events through the telemetry pipeline * @param events - The events to emit */ - public async emitEvents(events: BaseEvent[]): Promise { - try { - if (!this.isTelemetryEnabled()) { - this.session.logger.info({ - id: LogId.telemetryEmitFailure, - context: "telemetry", - message: "Telemetry is disabled.", - noRedaction: true, - }); - return; - } - - await this.emit(events); - } catch { - this.session.logger.debug({ - id: LogId.telemetryEmitFailure, - context: "telemetry", - message: "Error emitting telemetry events.", - noRedaction: true, - }); + public emitEvents(events: BaseEvent[]): void { + if (!this.isTelemetryEnabled()) { + this.events.emit("events-skipped"); + return; } + + // Don't wait for events to be sent - we should not block regular server + // operations on telemetry + void this.emit(events); } /** @@ -137,32 +172,44 @@ export class Telemetry { return; } - const cachedEvents = this.eventCache.getEvents(); - const allEvents = [...cachedEvents, ...events]; + try { + const cachedEvents = this.eventCache.getEvents(); + const allEvents = [...cachedEvents.map((e) => e.event), ...events]; - this.session.logger.debug({ - id: LogId.telemetryEmitStart, - context: "telemetry", - message: `Attempting to send ${allEvents.length} events (${cachedEvents.length} cached)`, - }); + this.session.logger.debug({ + id: LogId.telemetryEmitStart, + context: "telemetry", + message: `Attempting to send ${allEvents.length} events (${cachedEvents.length} cached)`, + }); + + const result = await this.sendEvents(this.session.apiClient, allEvents); + if (result.success) { + this.eventCache.removeEvents(cachedEvents.map((e) => e.id)); + this.session.logger.debug({ + id: LogId.telemetryEmitSuccess, + context: "telemetry", + message: `Sent ${allEvents.length} events successfully: ${JSON.stringify(allEvents)}`, + }); + this.events.emit("events-emitted"); + return; + } - const result = await this.sendEvents(this.session.apiClient, allEvents); - if (result.success) { - this.eventCache.clearEvents(); this.session.logger.debug({ - id: LogId.telemetryEmitSuccess, + id: LogId.telemetryEmitFailure, context: "telemetry", - message: `Sent ${allEvents.length} events successfully: ${JSON.stringify(allEvents, null, 2)}`, + message: `Error sending event to client: ${result.error instanceof Error ? result.error.message : String(result.error)}`, }); - return; + this.eventCache.appendEvents(events); + this.events.emit("events-send-failed"); + } catch (error) { + this.session.logger.debug({ + id: LogId.telemetryEmitFailure, + context: "telemetry", + message: `Error emitting telemetry events: ${error instanceof Error ? error.message : String(error)}`, + noRedaction: true, + }); + this.events.emit("events-send-failed"); } - - this.session.logger.debug({ - id: LogId.telemetryEmitFailure, - context: "telemetry", - message: `Error sending event to client: ${result.error instanceof Error ? result.error.message : String(result.error)}`, - }); - this.eventCache.appendEvents(events); } /** diff --git a/src/telemetry/types.ts b/src/telemetry/types.ts index f919ab88f..c1eced5af 100644 --- a/src/telemetry/types.ts +++ b/src/telemetry/types.ts @@ -45,6 +45,7 @@ export type ServerEventProperties = { runtime_duration_ms?: number; read_only_mode?: boolean; disabled_tools?: string[]; + confirmation_required_tools?: string[]; }; export type ServerEvent = TelemetryEvent; @@ -53,11 +54,34 @@ export type ServerEvent = TelemetryEvent; * Interface for static properties, they can be fetched once and reused. */ export type CommonStaticProperties = { + /** + * The version of the MCP server (as read from package.json). + */ mcp_server_version: string; + + /** + * The name of the MCP server (as read from package.json). + */ mcp_server_name: string; + + /** + * The platform/OS the MCP server is running on. + */ platform: string; + + /** + * The architecture of the OS the server is running on. + */ arch: string; + + /** + * Same as platform. + */ os_type: string; + + /** + * The version of the OS the server is running on. + */ os_version?: string; }; @@ -65,12 +89,50 @@ export type CommonStaticProperties = { * Common properties for all events that might change. */ export type CommonProperties = { + /** + * The device id - will be populated with the machine id when it resolves. + */ device_id?: string; + + /** + * A boolean indicating whether the server is running in a container environment. + */ is_container_env?: boolean; + + /** + * The version of the MCP client as reported by the client on session establishment. + */ mcp_client_version?: string; + + /** + * The name of the MCP client as reported by the client on session establishment. + */ mcp_client_name?: string; + + /** + * The transport protocol used by the MCP server. + */ transport?: "stdio" | "http"; + + /** + * A boolean indicating whether Atlas credentials are configured. + */ config_atlas_auth?: TelemetryBoolSet; + + /** + * A boolean indicating whether a connection string is configured. + */ config_connection_string?: TelemetryBoolSet; + + /** + * The randomly generated session id. + */ session_id?: string; + + /** + * The way the MCP server is hosted - e.g. standalone for a server running independently or + * "vscode" if embedded in the VSCode extension. This field should be populated by the hosting + * application to differentiate events coming from an MCP server it's hosting. + */ + hosting_mode?: string; } & CommonStaticProperties; diff --git a/src/tools/args.ts b/src/tools/args.ts new file mode 100644 index 000000000..165f3da0d --- /dev/null +++ b/src/tools/args.ts @@ -0,0 +1,70 @@ +import { z, type ZodString } from "zod"; + +const NO_UNICODE_REGEX = /^[\x20-\x7E]*$/; +export const NO_UNICODE_ERROR = "String cannot contain special characters or Unicode symbols"; + +const ALLOWED_USERNAME_CHARACTERS_REGEX = /^[a-zA-Z0-9._-]+$/; +export const ALLOWED_USERNAME_CHARACTERS_ERROR = + "Username can only contain letters, numbers, dots, hyphens, and underscores"; + +const ALLOWED_REGION_CHARACTERS_REGEX = /^[a-zA-Z0-9_-]+$/; +export const ALLOWED_REGION_CHARACTERS_ERROR = "Region can only contain letters, numbers, hyphens, and underscores"; + +const ALLOWED_CLUSTER_NAME_CHARACTERS_REGEX = /^[a-zA-Z0-9_-]+$/; +export const ALLOWED_CLUSTER_NAME_CHARACTERS_ERROR = + "Cluster names can only contain ASCII letters, numbers, and hyphens."; + +const ALLOWED_PROJECT_NAME_CHARACTERS_REGEX = /^[a-zA-Z0-9\s()@&+:._',-]+$/; +export const ALLOWED_PROJECT_NAME_CHARACTERS_ERROR = + "Project names can't be longer than 64 characters and can only contain letters, numbers, spaces, and the following symbols: ( ) @ & + : . _ - ' ,"; +export const CommonArgs = { + string: (): ZodString => z.string().regex(NO_UNICODE_REGEX, NO_UNICODE_ERROR), + + objectId: (fieldName: string): z.ZodString => + z + .string() + .min(1, `${fieldName} is required`) + .length(24, `${fieldName} must be exactly 24 characters`) + .regex(/^[0-9a-fA-F]+$/, `${fieldName} must contain only hexadecimal characters`), +}; + +export const AtlasArgs = { + projectId: (): z.ZodString => CommonArgs.objectId("projectId"), + + organizationId: (): z.ZodString => CommonArgs.objectId("organizationId"), + + clusterName: (): z.ZodString => + z + .string() + .min(1, "Cluster name is required") + .max(64, "Cluster name must be 64 characters or less") + .regex(ALLOWED_CLUSTER_NAME_CHARACTERS_REGEX, ALLOWED_CLUSTER_NAME_CHARACTERS_ERROR), + + projectName: (): z.ZodString => + z + .string() + .min(1, "Project name is required") + .max(64, "Project name must be 64 characters or less") + .regex(ALLOWED_PROJECT_NAME_CHARACTERS_REGEX, ALLOWED_PROJECT_NAME_CHARACTERS_ERROR), + + username: (): z.ZodString => + z + .string() + .min(1, "Username is required") + .max(100, "Username must be 100 characters or less") + .regex(ALLOWED_USERNAME_CHARACTERS_REGEX, ALLOWED_USERNAME_CHARACTERS_ERROR), + + ipAddress: (): z.ZodString => z.string().ip({ version: "v4" }), + + cidrBlock: (): z.ZodString => z.string().cidr(), + + region: (): z.ZodString => + z + .string() + .min(1, "Region is required") + .max(50, "Region must be 50 characters or less") + .regex(ALLOWED_REGION_CHARACTERS_REGEX, ALLOWED_REGION_CHARACTERS_ERROR), + + password: (): z.ZodString => + z.string().min(1, "Password is required").max(100, "Password must be 100 characters or less"), +}; diff --git a/src/tools/atlas/atlasTool.ts b/src/tools/atlas/atlasTool.ts index 452f2e794..b68eeafde 100644 --- a/src/tools/atlas/atlasTool.ts +++ b/src/tools/atlas/atlasTool.ts @@ -1,6 +1,5 @@ -import type { ToolCategory, TelemetryToolMetadata, ToolArgs } from "../tool.js"; -import { ToolBase } from "../tool.js"; import type { ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { ToolBase, type ToolArgs, type ToolCategory, type TelemetryToolMetadata } from "../tool.js"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { LogId } from "../../common/logger.js"; import { z } from "zod"; diff --git a/src/tools/atlas/connect/connectCluster.ts b/src/tools/atlas/connect/connectCluster.ts index 9695ff369..54f3ae8bd 100644 --- a/src/tools/atlas/connect/connectCluster.ts +++ b/src/tools/atlas/connect/connectCluster.ts @@ -1,27 +1,35 @@ -import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { type OperationType, type ToolArgs } from "../../tool.js"; import { AtlasToolBase } from "../atlasTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; import { generateSecurePassword } from "../../../helpers/generatePassword.js"; import { LogId } from "../../../common/logger.js"; import { inspectCluster } from "../../../common/atlas/cluster.js"; import { ensureCurrentIpInAccessList } from "../../../common/atlas/accessListUtils.js"; import type { AtlasClusterConnectionInfo } from "../../../common/connectionManager.js"; import { getDefaultRoleFromConfig } from "../../../common/atlas/roles.js"; +import { AtlasArgs } from "../../args.js"; -const EXPIRY_MS = 1000 * 60 * 60 * 12; // 12 hours +const addedIpAccessListMessage = + "Note: Your current IP address has been added to the Atlas project's IP access list to enable secure connection."; + +const createdUserMessage = + "Note: A temporary user has been created to enable secure connection to the cluster. For more information, see https://dochub.mongodb.org/core/mongodb-mcp-server-tools-considerations\n\nNote to LLM Agent: it is important to include the following link in your response to the user in case they want to get more information about the temporary user created: https://dochub.mongodb.org/core/mongodb-mcp-server-tools-considerations"; function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } +export const ConnectClusterArgs = { + projectId: AtlasArgs.projectId().describe("Atlas project ID"), + clusterName: AtlasArgs.clusterName().describe("Atlas cluster name"), +}; + export class ConnectClusterTool extends AtlasToolBase { public name = "atlas-connect-cluster"; protected description = "Connect to MongoDB Atlas cluster"; public operationType: OperationType = "connect"; protected argsShape = { - projectId: z.string().describe("Atlas project ID"), - clusterName: z.string().describe("Atlas cluster name"), + ...ConnectClusterArgs, }; private queryConnection( @@ -72,7 +80,7 @@ export class ConnectClusterTool extends AtlasToolBase { const username = `mcpUser${Math.floor(Math.random() * 100000)}`; const password = await generateSecurePassword(); - const expiryDate = new Date(Date.now() + EXPIRY_MS); + const expiryDate = new Date(Date.now() + this.config.atlasTemporaryDatabaseUserLifetimeMs); const role = getDefaultRoleFromConfig(this.config); await this.session.apiClient.createDatabaseUser({ @@ -110,6 +118,9 @@ export class ConnectClusterTool extends AtlasToolBase { cn.password = password; cn.searchParams.set("authSource", "admin"); + this.session.keychain.register(username, "user"); + this.session.keychain.register(password, "password"); + return { connectionString: cn.toString(), atlas: connectedAtlasCluster }; } @@ -190,19 +201,35 @@ export class ConnectClusterTool extends AtlasToolBase { } protected async execute({ projectId, clusterName }: ToolArgs): Promise { - await ensureCurrentIpInAccessList(this.session.apiClient, projectId); + const ipAccessListUpdated = await ensureCurrentIpInAccessList(this.session.apiClient, projectId); + let createdUser = false; + for (let i = 0; i < 60; i++) { const state = this.queryConnection(projectId, clusterName); switch (state) { case "connected": { - return { - content: [ - { - type: "text", - text: `Connected to cluster "${clusterName}".`, - }, - ], - }; + const content: CallToolResult["content"] = [ + { + type: "text", + text: `Connected to cluster "${clusterName}".`, + }, + ]; + + if (ipAccessListUpdated) { + content.push({ + type: "text", + text: addedIpAccessListMessage, + }); + } + + if (createdUser) { + content.push({ + type: "text", + text: createdUserMessage, + }); + } + + return { content }; } case "connecting": case "unknown": { @@ -214,6 +241,7 @@ export class ConnectClusterTool extends AtlasToolBase { await this.session.disconnect(); const { connectionString, atlas } = await this.prepareClusterConnection(projectId, clusterName); + createdUser = true; // try to connect for about 5 minutes asynchronously void this.connectToCluster(connectionString, atlas).catch((err: unknown) => { const error = err instanceof Error ? err : new Error(String(err)); @@ -230,21 +258,31 @@ export class ConnectClusterTool extends AtlasToolBase { await sleep(500); } - return { - content: [ - { - type: "text" as const, - text: `Attempting to connect to cluster "${clusterName}"...`, - }, - { - type: "text" as const, - text: `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.`, - }, - { - type: "text" as const, - text: `Warning: Make sure your IP address was enabled in the allow list setting of the Atlas cluster.`, - }, - ], - }; + const content: CallToolResult["content"] = [ + { + type: "text" as const, + text: `Attempting to connect to cluster "${clusterName}"...`, + }, + { + type: "text" as const, + text: `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.`, + }, + ]; + + if (ipAccessListUpdated) { + content.push({ + type: "text" as const, + text: addedIpAccessListMessage, + }); + } + + if (createdUser) { + content.push({ + type: "text" as const, + text: createdUserMessage, + }); + } + + return { content }; } } diff --git a/src/tools/atlas/create/createAccessList.ts b/src/tools/atlas/create/createAccessList.ts index c7f5d43d8..fe5a862ff 100644 --- a/src/tools/atlas/create/createAccessList.ts +++ b/src/tools/atlas/create/createAccessList.ts @@ -1,26 +1,27 @@ import { z } from "zod"; +import { type OperationType, type ToolArgs } from "../../tool.js"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { AtlasToolBase } from "../atlasTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; import { makeCurrentIpAccessListEntry, DEFAULT_ACCESS_LIST_COMMENT } from "../../../common/atlas/accessListUtils.js"; +import { AtlasArgs, CommonArgs } from "../../args.js"; + +export const CreateAccessListArgs = { + projectId: AtlasArgs.projectId().describe("Atlas project ID"), + ipAddresses: z.array(AtlasArgs.ipAddress()).describe("IP addresses to allow access from").optional(), + cidrBlocks: z.array(AtlasArgs.cidrBlock()).describe("CIDR blocks to allow access from").optional(), + currentIpAddress: z.boolean().describe("Add the current IP address").default(false), + comment: CommonArgs.string() + .describe("Comment for the access list entries") + .default(DEFAULT_ACCESS_LIST_COMMENT) + .optional(), +}; export class CreateAccessListTool extends AtlasToolBase { public name = "atlas-create-access-list"; protected description = "Allow Ip/CIDR ranges to access your MongoDB Atlas clusters."; public operationType: OperationType = "create"; protected argsShape = { - projectId: z.string().describe("Atlas project ID"), - ipAddresses: z - .array(z.string().ip({ version: "v4" })) - .describe("IP addresses to allow access from") - .optional(), - cidrBlocks: z.array(z.string().cidr()).describe("CIDR blocks to allow access from").optional(), - currentIpAddress: z.boolean().describe("Add the current IP address").default(false), - comment: z - .string() - .describe("Comment for the access list entries") - .default(DEFAULT_ACCESS_LIST_COMMENT) - .optional(), + ...CreateAccessListArgs, }; protected async execute({ @@ -75,4 +76,31 @@ export class CreateAccessListTool extends AtlasToolBase { ], }; } + + protected getConfirmationMessage({ + projectId, + ipAddresses, + cidrBlocks, + comment, + currentIpAddress, + }: ToolArgs): string { + const accessDescription = []; + if (ipAddresses?.length) { + accessDescription.push(`- **IP addresses**: ${ipAddresses.join(", ")}`); + } + if (cidrBlocks?.length) { + accessDescription.push(`- **CIDR blocks**: ${cidrBlocks.join(", ")}`); + } + if (currentIpAddress) { + accessDescription.push("- **Current IP address**"); + } + + return ( + `You are about to add the following entries to the access list for Atlas project "${projectId}":\n\n` + + accessDescription.join("\n") + + `\n\n**Comment**: ${comment || DEFAULT_ACCESS_LIST_COMMENT}\n\n` + + "This will allow network access to your MongoDB Atlas clusters from these IP addresses/ranges. " + + "Do you want to proceed?" + ); + } } diff --git a/src/tools/atlas/create/createDBUser.ts b/src/tools/atlas/create/createDBUser.ts index 98d956980..c8e8ea014 100644 --- a/src/tools/atlas/create/createDBUser.ts +++ b/src/tools/atlas/create/createDBUser.ts @@ -1,41 +1,44 @@ import { z } from "zod"; +import type { ToolArgs, OperationType } from "../../tool.js"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { AtlasToolBase } from "../atlasTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; import type { CloudDatabaseUser, DatabaseUserRole } from "../../../common/atlas/openapi.js"; import { generateSecurePassword } from "../../../helpers/generatePassword.js"; import { ensureCurrentIpInAccessList } from "../../../common/atlas/accessListUtils.js"; +import { AtlasArgs, CommonArgs } from "../../args.js"; + +export const CreateDBUserArgs = { + projectId: AtlasArgs.projectId().describe("Atlas project ID"), + username: AtlasArgs.username().describe("Username for the new user"), + // Models will generate overly simplistic passwords like SecurePassword123 or + // AtlasPassword123, which are easily guessable and exploitable. We're instructing + // the model not to try and generate anything and instead leave the field unset. + password: AtlasArgs.password() + .nullish() + .describe( + "Password for the new user. IMPORTANT: If the user hasn't supplied an explicit password, leave it unset and under no circumstances try to generate a random one. A secure password will be generated by the MCP server if necessary." + ), + roles: z + .array( + z.object({ + roleName: CommonArgs.string().describe("Role name"), + databaseName: CommonArgs.string().describe("Database name").default("admin"), + collectionName: CommonArgs.string().describe("Collection name").optional(), + }) + ) + .describe("Roles for the new user"), + clusters: z + .array(AtlasArgs.clusterName()) + .describe("Clusters to assign the user to, leave empty for access to all clusters") + .optional(), +}; export class CreateDBUserTool extends AtlasToolBase { public name = "atlas-create-db-user"; protected description = "Create an MongoDB Atlas database user"; public operationType: OperationType = "create"; protected argsShape = { - projectId: z.string().describe("Atlas project ID"), - username: z.string().describe("Username for the new user"), - // Models will generate overly simplistic passwords like SecurePassword123 or - // AtlasPassword123, which are easily guessable and exploitable. We're instructing - // the model not to try and generate anything and instead leave the field unset. - password: z - .string() - .optional() - .nullable() - .describe( - "Password for the new user. IMPORTANT: If the user hasn't supplied an explicit password, leave it unset and under no circumstances try to generate a random one. A secure password will be generated by the MCP server if necessary." - ), - roles: z - .array( - z.object({ - roleName: z.string().describe("Role name"), - databaseName: z.string().describe("Database name").default("admin"), - collectionName: z.string().describe("Collection name").optional(), - }) - ) - .describe("Roles for the new user"), - clusters: z - .array(z.string()) - .describe("Clusters to assign the user to, leave empty for access to all clusters") - .optional(), + ...CreateDBUserArgs, }; protected async execute({ @@ -78,6 +81,11 @@ export class CreateDBUserTool extends AtlasToolBase { body: input, }); + this.session.keychain.register(username, "user"); + if (password) { + this.session.keychain.register(password, "password"); + } + return { content: [ { @@ -87,4 +95,22 @@ export class CreateDBUserTool extends AtlasToolBase { ], }; } + + protected getConfirmationMessage({ + projectId, + username, + password, + roles, + clusters, + }: ToolArgs): string { + return ( + `You are about to create a database user in Atlas project \`${projectId}\`:\n\n` + + `**Username**: \`${username}\`\n\n` + + `**Password**: ${password ? "(User-provided password)" : "(Auto-generated secure password)"}\n\n` + + `**Roles**: ${roles.map((role) => `${role.roleName}${role.collectionName ? ` on ${role.databaseName}.${role.collectionName}` : ` on ${role.databaseName}`}`).join(", ")}\n\n` + + `**Cluster Access**: ${clusters?.length ? clusters.join(", ") : "All clusters in the project"}\n\n` + + "This will create a new database user with the specified permissions. " + + "**Do you confirm the execution of the action?**" + ); + } } diff --git a/src/tools/atlas/create/createFreeCluster.ts b/src/tools/atlas/create/createFreeCluster.ts index 5a110d95d..6b1ac98eb 100644 --- a/src/tools/atlas/create/createFreeCluster.ts +++ b/src/tools/atlas/create/createFreeCluster.ts @@ -1,18 +1,18 @@ -import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { type ToolArgs, type OperationType } from "../../tool.js"; import { AtlasToolBase } from "../atlasTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; import type { ClusterDescription20240805 } from "../../../common/atlas/openapi.js"; import { ensureCurrentIpInAccessList } from "../../../common/atlas/accessListUtils.js"; +import { AtlasArgs } from "../../args.js"; export class CreateFreeClusterTool extends AtlasToolBase { public name = "atlas-create-free-cluster"; protected description = "Create a free MongoDB Atlas cluster"; public operationType: OperationType = "create"; protected argsShape = { - projectId: z.string().describe("Atlas project ID to create the cluster in"), - name: z.string().describe("Name of the cluster"), - region: z.string().describe("Region of the cluster").default("US_EAST_1"), + projectId: AtlasArgs.projectId().describe("Atlas project ID to create the cluster in"), + name: AtlasArgs.clusterName().describe("Name of the cluster"), + region: AtlasArgs.region().describe("Region of the cluster").default("US_EAST_1"), }; protected async execute({ projectId, name, region }: ToolArgs): Promise { diff --git a/src/tools/atlas/create/createProject.ts b/src/tools/atlas/create/createProject.ts index 60753b6b0..b981fd8e8 100644 --- a/src/tools/atlas/create/createProject.ts +++ b/src/tools/atlas/create/createProject.ts @@ -1,16 +1,20 @@ -import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { type OperationType, type ToolArgs } from "../../tool.js"; import { AtlasToolBase } from "../atlasTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; import type { Group } from "../../../common/atlas/openapi.js"; +import { AtlasArgs } from "../../args.js"; + +export const CreateProjectArgs = { + projectName: AtlasArgs.projectName().optional().describe("Name for the new project"), + organizationId: AtlasArgs.organizationId().optional().describe("Organization ID for the new project"), +}; export class CreateProjectTool extends AtlasToolBase { public name = "atlas-create-project"; protected description = "Create a MongoDB Atlas project"; public operationType: OperationType = "create"; protected argsShape = { - projectName: z.string().optional().describe("Name for the new project"), - organizationId: z.string().optional().describe("Organization ID for the new project"), + ...CreateProjectArgs, }; protected async execute({ projectName, organizationId }: ToolArgs): Promise { diff --git a/src/tools/atlas/read/inspectAccessList.ts b/src/tools/atlas/read/inspectAccessList.ts index 7eedf6ed7..6c8eaed30 100644 --- a/src/tools/atlas/read/inspectAccessList.ts +++ b/src/tools/atlas/read/inspectAccessList.ts @@ -1,15 +1,18 @@ -import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { type OperationType, type ToolArgs, formatUntrustedData } from "../../tool.js"; import { AtlasToolBase } from "../atlasTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; -import { formatUntrustedData } from "../../tool.js"; +import { AtlasArgs } from "../../args.js"; + +export const InspectAccessListArgs = { + projectId: AtlasArgs.projectId().describe("Atlas project ID"), +}; export class InspectAccessListTool extends AtlasToolBase { public name = "atlas-inspect-access-list"; protected description = "Inspect Ip/CIDR ranges with access to your MongoDB Atlas clusters."; public operationType: OperationType = "read"; protected argsShape = { - projectId: z.string().describe("Atlas project ID"), + ...InspectAccessListArgs, }; protected async execute({ projectId }: ToolArgs): Promise { diff --git a/src/tools/atlas/read/inspectCluster.ts b/src/tools/atlas/read/inspectCluster.ts index feb5f5ac2..56e1e5a8b 100644 --- a/src/tools/atlas/read/inspectCluster.ts +++ b/src/tools/atlas/read/inspectCluster.ts @@ -1,18 +1,21 @@ -import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { type OperationType, type ToolArgs, formatUntrustedData } from "../../tool.js"; import { AtlasToolBase } from "../atlasTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; -import { formatUntrustedData } from "../../tool.js"; import type { Cluster } from "../../../common/atlas/cluster.js"; import { inspectCluster } from "../../../common/atlas/cluster.js"; +import { AtlasArgs } from "../../args.js"; + +export const InspectClusterArgs = { + projectId: AtlasArgs.projectId().describe("Atlas project ID"), + clusterName: AtlasArgs.clusterName().describe("Atlas cluster name"), +}; export class InspectClusterTool extends AtlasToolBase { public name = "atlas-inspect-cluster"; protected description = "Inspect MongoDB Atlas cluster"; public operationType: OperationType = "read"; protected argsShape = { - projectId: z.string().describe("Atlas project ID"), - clusterName: z.string().describe("Atlas cluster name"), + ...InspectClusterArgs, }; protected async execute({ projectId, clusterName }: ToolArgs): Promise { diff --git a/src/tools/atlas/read/listAlerts.ts b/src/tools/atlas/read/listAlerts.ts index 8ab4666c7..d55a917f8 100644 --- a/src/tools/atlas/read/listAlerts.ts +++ b/src/tools/atlas/read/listAlerts.ts @@ -1,15 +1,18 @@ -import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { type OperationType, type ToolArgs, formatUntrustedData } from "../../tool.js"; import { AtlasToolBase } from "../atlasTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; -import { formatUntrustedData } from "../../tool.js"; +import { AtlasArgs } from "../../args.js"; + +export const ListAlertsArgs = { + projectId: AtlasArgs.projectId().describe("Atlas project ID to list alerts for"), +}; export class ListAlertsTool extends AtlasToolBase { public name = "atlas-list-alerts"; protected description = "List MongoDB Atlas alerts"; public operationType: OperationType = "read"; protected argsShape = { - projectId: z.string().describe("Atlas project ID to list alerts for"), + ...ListAlertsArgs, }; protected async execute({ projectId }: ToolArgs): Promise { diff --git a/src/tools/atlas/read/listClusters.ts b/src/tools/atlas/read/listClusters.ts index e3894b3f6..60344f7d3 100644 --- a/src/tools/atlas/read/listClusters.ts +++ b/src/tools/atlas/read/listClusters.ts @@ -1,4 +1,3 @@ -import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { AtlasToolBase } from "../atlasTool.js"; import type { ToolArgs, OperationType } from "../../tool.js"; @@ -10,13 +9,18 @@ import type { PaginatedFlexClusters20241113, } from "../../../common/atlas/openapi.js"; import { formatCluster, formatFlexCluster } from "../../../common/atlas/cluster.js"; +import { AtlasArgs } from "../../args.js"; + +export const ListClustersArgs = { + projectId: AtlasArgs.projectId().describe("Atlas project ID to filter clusters").optional(), +}; export class ListClustersTool extends AtlasToolBase { public name = "atlas-list-clusters"; protected description = "List MongoDB Atlas clusters"; public operationType: OperationType = "read"; protected argsShape = { - projectId: z.string().describe("Atlas project ID to filter clusters").optional(), + ...ListClustersArgs, }; protected async execute({ projectId }: ToolArgs): Promise { @@ -93,17 +97,17 @@ ${rows}`, } const formattedClusters = clusters?.results?.map((cluster) => formatCluster(cluster)) || []; const formattedFlexClusters = flexClusters?.results?.map((cluster) => formatFlexCluster(cluster)) || []; - const rows = [...formattedClusters, ...formattedFlexClusters] - .map((formattedCluster) => { - return `${formattedCluster.name || "Unknown"} | ${formattedCluster.instanceType} | ${formattedCluster.instanceSize || "N/A"} | ${formattedCluster.state || "UNKNOWN"} | ${formattedCluster.mongoDBVersion || "N/A"} | ${formattedCluster.connectionString || "N/A"}`; - }) - .join("\n"); + const allClusters = [...formattedClusters, ...formattedFlexClusters]; return { content: formatUntrustedData( - `Found ${rows.length} clusters in project "${project.name}" (${project.id}):`, + `Found ${allClusters.length} clusters in project "${project.name}" (${project.id}):`, `Cluster Name | Cluster Type | Tier | State | MongoDB Version | Connection String ----------------|----------------|----------------|----------------|----------------|---------------- -${rows}` +${allClusters + .map((formattedCluster) => { + return `${formattedCluster.name || "Unknown"} | ${formattedCluster.instanceType} | ${formattedCluster.instanceSize || "N/A"} | ${formattedCluster.state || "UNKNOWN"} | ${formattedCluster.mongoDBVersion || "N/A"} | ${formattedCluster.connectionString || "N/A"}`; + }) + .join("\n")}` ), }; } diff --git a/src/tools/atlas/read/listDBUsers.ts b/src/tools/atlas/read/listDBUsers.ts index 26bb28b93..5ab23250c 100644 --- a/src/tools/atlas/read/listDBUsers.ts +++ b/src/tools/atlas/read/listDBUsers.ts @@ -1,16 +1,20 @@ -import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { AtlasToolBase } from "../atlasTool.js"; import type { ToolArgs, OperationType } from "../../tool.js"; import { formatUntrustedData } from "../../tool.js"; import type { DatabaseUserRole, UserScope } from "../../../common/atlas/openapi.js"; +import { AtlasArgs } from "../../args.js"; + +export const ListDBUsersArgs = { + projectId: AtlasArgs.projectId().describe("Atlas project ID to filter DB users"), +}; export class ListDBUsersTool extends AtlasToolBase { public name = "atlas-list-db-users"; protected description = "List MongoDB Atlas database users"; public operationType: OperationType = "read"; protected argsShape = { - projectId: z.string().describe("Atlas project ID to filter DB users"), + ...ListDBUsersArgs, }; protected async execute({ projectId }: ToolArgs): Promise { diff --git a/src/tools/atlas/read/listProjects.ts b/src/tools/atlas/read/listProjects.ts index 720186ecf..3b7d24939 100644 --- a/src/tools/atlas/read/listProjects.ts +++ b/src/tools/atlas/read/listProjects.ts @@ -2,15 +2,19 @@ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { AtlasToolBase } from "../atlasTool.js"; import type { OperationType } from "../../tool.js"; import { formatUntrustedData } from "../../tool.js"; -import { z } from "zod"; import type { ToolArgs } from "../../tool.js"; +import { AtlasArgs } from "../../args.js"; + +export const ListProjectsArgs = { + orgId: AtlasArgs.organizationId().describe("Atlas organization ID to filter projects").optional(), +}; export class ListProjectsTool extends AtlasToolBase { public name = "atlas-list-projects"; protected description = "List MongoDB Atlas projects"; public operationType: OperationType = "read"; protected argsShape = { - orgId: z.string().describe("Atlas organization ID to filter projects").optional(), + ...ListProjectsArgs, }; protected async execute({ orgId }: ToolArgs): Promise { @@ -55,7 +59,7 @@ export class ListProjectsTool extends AtlasToolBase { ----------------| ----------------| ----------------| ----------------| ---------------- ${rows}`; return { - content: formatUntrustedData(`Found ${rows.length} projects`, formattedProjects), + content: formatUntrustedData(`Found ${data.results.length} projects`, formattedProjects), }; } } diff --git a/src/tools/mongodb/connect/connect.ts b/src/tools/mongodb/connect/connect.ts index 3fd6b48c3..d7ed16d26 100644 --- a/src/tools/mongodb/connect/connect.ts +++ b/src/tools/mongodb/connect/connect.ts @@ -1,11 +1,8 @@ import { z } from "zod"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { MongoDBToolBase } from "../mongodbTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; +import type { ToolArgs, OperationType, ToolConstructorParams } from "../../tool.js"; import assert from "assert"; -import type { UserConfig } from "../../../common/config.js"; -import type { Telemetry } from "../../../telemetry/telemetry.js"; -import type { Session } from "../../../common/session.js"; import type { Server } from "../../../server.js"; const disconnectedSchema = z @@ -44,8 +41,8 @@ export class ConnectTool extends MongoDBToolBase { public operationType: OperationType = "connect"; - constructor(session: Session, config: UserConfig, telemetry: Telemetry) { - super(session, config, telemetry); + constructor({ session, config, telemetry, elicitation }: ToolConstructorParams) { + super({ session, config, telemetry, elicitation }); session.on("connect", () => { this.updateMetadata(); }); diff --git a/src/tools/mongodb/delete/deleteMany.ts b/src/tools/mongodb/delete/deleteMany.ts index 3f769f3ab..754b0381a 100644 --- a/src/tools/mongodb/delete/deleteMany.ts +++ b/src/tools/mongodb/delete/deleteMany.ts @@ -3,6 +3,7 @@ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; import type { ToolArgs, OperationType } from "../../tool.js"; import { checkIndexUsage } from "../../../helpers/indexCheck.js"; +import { EJSON } from "bson"; export class DeleteManyTool extends MongoDBToolBase { public name = "delete-many"; @@ -55,4 +56,17 @@ export class DeleteManyTool extends MongoDBToolBase { ], }; } + + protected getConfirmationMessage({ database, collection, filter }: ToolArgs): string { + const filterDescription = + filter && Object.keys(filter).length > 0 + ? "```json\n" + `{ "filter": ${EJSON.stringify(filter)} }\n` + "```\n\n" + : "- **All documents** (No filter)\n\n"; + return ( + `You are about to delete documents from the \`${collection}\` collection in the \`${database}\` database:\n\n` + + filterDescription + + "This operation will permanently remove all documents matching the filter.\n\n" + + "**Do you confirm the execution of the action?**" + ); + } } diff --git a/src/tools/mongodb/delete/dropCollection.ts b/src/tools/mongodb/delete/dropCollection.ts index ea46355ca..50bd008a7 100644 --- a/src/tools/mongodb/delete/dropCollection.ts +++ b/src/tools/mongodb/delete/dropCollection.ts @@ -24,4 +24,12 @@ export class DropCollectionTool extends MongoDBToolBase { ], }; } + + protected getConfirmationMessage({ database, collection }: ToolArgs): string { + return ( + `You are about to drop the \`${collection}\` collection from the \`${database}\` database:\n\n` + + "This operation will permanently remove the collection and all its data, including indexes.\n\n" + + "**Do you confirm the execution of the action?**" + ); + } } diff --git a/src/tools/mongodb/delete/dropDatabase.ts b/src/tools/mongodb/delete/dropDatabase.ts index b877bf67c..d33682ce3 100644 --- a/src/tools/mongodb/delete/dropDatabase.ts +++ b/src/tools/mongodb/delete/dropDatabase.ts @@ -23,4 +23,12 @@ export class DropDatabaseTool extends MongoDBToolBase { ], }; } + + protected getConfirmationMessage({ database }: ToolArgs): string { + return ( + `You are about to drop the \`${database}\` database:\n\n` + + "This operation will permanently remove the database and ALL its collections, documents, and indexes.\n\n" + + "**Do you confirm the execution of the action?**" + ); + } } diff --git a/src/tools/mongodb/metadata/logs.ts b/src/tools/mongodb/metadata/logs.ts index 844d0283f..b19fa72c2 100644 --- a/src/tools/mongodb/metadata/logs.ts +++ b/src/tools/mongodb/metadata/logs.ts @@ -1,6 +1,6 @@ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { MongoDBToolBase } from "../mongodbTool.js"; -import type { ToolArgs, OperationType } from "../../tool.js"; +import { type ToolArgs, type OperationType, formatUntrustedData } from "../../tool.js"; import { z } from "zod"; export class LogsTool extends MongoDBToolBase { @@ -33,23 +33,16 @@ export class LogsTool extends MongoDBToolBase { getLog: type, }); - const logs = (result.log as string[]).slice(0, limit); + // Trim ending newlines so that when we join the logs we don't insert empty lines + // between messages. + const logs = (result.log as string[]).slice(0, limit).map((l) => l.trimEnd()); + let message = `Found: ${result.totalLinesWritten} messages`; + if (result.totalLinesWritten > limit) { + message += ` (showing only the first ${limit})`; + } return { - content: [ - { - text: `Found: ${result.totalLinesWritten} messages`, - type: "text", - }, - - ...logs.map( - (log) => - ({ - text: log, - type: "text", - }) as const - ), - ], + content: formatUntrustedData(message, logs.join("\n")), }; } } diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index 5fff778a6..ded994ab3 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -56,63 +56,22 @@ export abstract class MongoDBToolBase extends ToolBase { args: ToolArgs ): Promise | CallToolResult { if (error instanceof MongoDBError) { - const connectTools = this.server?.tools - .filter((t) => t.operationType === "connect") - .sort((a, b) => a.category.localeCompare(b.category)); // Sort Altas tools before MongoDB tools - - // Find the first Atlas connect tool if available and suggest to the LLM to use it. - // Note: if we ever have multiple Atlas connect tools, we may want to refine this logic to select the most appropriate one. - const atlasConnectTool = connectTools?.find((t) => t.category === "atlas"); - const llmConnectHint = atlasConnectTool - ? `Note to LLM: prefer using the "${atlasConnectTool.name}" tool to connect to an Atlas cluster over using a connection string. Make sure to ask the user to specify a cluster name they want to connect to or ask them if they want to use the "list-clusters" tool to list all their clusters. Do not invent cluster names or connection strings unless the user has explicitly specified them. If they've previously connected to MongoDB using MCP, you can ask them if they want to reconnect using the same cluster/connection.` - : "Note to LLM: do not invent connection strings and explicitly ask the user to provide one. If they have previously connected to MongoDB using MCP, you can ask them if they want to reconnect using the same connection string."; - - const connectToolsNames = connectTools?.map((t) => `"${t.name}"`).join(", "); - const connectionStatus = this.session.connectionManager.currentConnectionState; - const additionalPromptForConnectivity: { type: "text"; text: string }[] = []; - - if (connectionStatus.tag === "connecting" && connectionStatus.oidcConnectionType) { - additionalPromptForConnectivity.push({ - type: "text", - text: `The user needs to finish their OIDC connection by opening '${connectionStatus.oidcLoginUrl}' in the browser and use the following user code: '${connectionStatus.oidcUserCode}'`, - }); - } else { - additionalPromptForConnectivity.push({ - type: "text", - text: connectToolsNames - ? `Please use one of the following tools: ${connectToolsNames} to connect to a MongoDB instance or update the MCP server configuration to include a connection string. ${llmConnectHint}` - : "There are no tools available to connect. Please update the configuration to include a connection string and restart the server.", - }); - } - switch (error.code) { case ErrorCodes.NotConnectedToMongoDB: - return { - content: [ - { - type: "text", - text: "You need to connect to a MongoDB instance before you can access its data.", - }, - ...additionalPromptForConnectivity, - ], - isError: true, - }; - case ErrorCodes.MisconfiguredConnectionString: - return { - content: [ - { - type: "text", - text: "The configured connection string is not valid. Please check the connection string and confirm it points to a valid MongoDB instance.", - }, - { - type: "text", - text: connectTools - ? `Alternatively, you can use one of the following tools: ${connectToolsNames} to connect to a MongoDB instance. ${llmConnectHint}` - : "Please update the configuration to use a valid connection string and restart the server.", - }, - ], - isError: true, - }; + case ErrorCodes.MisconfiguredConnectionString: { + const connectionError = error as MongoDBError< + ErrorCodes.NotConnectedToMongoDB | ErrorCodes.MisconfiguredConnectionString + >; + const outcome = this.server?.connectionErrorHandler(connectionError, { + availableTools: this.server?.tools ?? [], + connectionState: this.session.connectionManager.currentConnectionState, + }); + if (outcome?.errorHandled) { + return outcome.result; + } + + return super.handleError(error, args); + } case ErrorCodes.ForbiddenCollscan: return { content: [ diff --git a/src/tools/mongodb/read/aggregate.ts b/src/tools/mongodb/read/aggregate.ts index 8492a61ce..45df45471 100644 --- a/src/tools/mongodb/read/aggregate.ts +++ b/src/tools/mongodb/read/aggregate.ts @@ -5,6 +5,7 @@ import type { ToolArgs, OperationType } from "../../tool.js"; import { formatUntrustedData } from "../../tool.js"; import { checkIndexUsage } from "../../../helpers/indexCheck.js"; import { EJSON } from "bson"; +import { ErrorCodes, MongoDBError } from "../../../common/errors.js"; export const AggregateArgs = { pipeline: z.array(z.object({}).passthrough()).describe("An array of aggregation stages to execute"), @@ -26,6 +27,8 @@ export class AggregateTool extends MongoDBToolBase { }: ToolArgs): Promise { const provider = await this.ensureConnected(); + this.assertOnlyUsesPermittedStages(pipeline); + // Check if aggregate operation uses an index if enabled if (this.config.indexCheck) { await checkIndexUsage(provider, database, collection, "aggregate", async () => { @@ -44,4 +47,26 @@ export class AggregateTool extends MongoDBToolBase { ), }; } + + private assertOnlyUsesPermittedStages(pipeline: Record[]): void { + const writeOperations: OperationType[] = ["update", "create", "delete"]; + let writeStageForbiddenError = ""; + + if (this.config.readOnly) { + writeStageForbiddenError = "In readOnly mode you can not run pipelines with $out or $merge stages."; + } else if (this.config.disabledTools.some((t) => writeOperations.includes(t as OperationType))) { + writeStageForbiddenError = + "When 'create', 'update', or 'delete' operations are disabled, you can not run pipelines with $out or $merge stages."; + } + + if (!writeStageForbiddenError) { + return; + } + + for (const stage of pipeline) { + if (stage.$out || stage.$merge) { + throw new MongoDBError(ErrorCodes.ForbiddenWriteOperation, writeStageForbiddenError); + } + } + } } diff --git a/src/tools/mongodb/read/export.ts b/src/tools/mongodb/read/export.ts index 784f0e14f..e2ac194b3 100644 --- a/src/tools/mongodb/read/export.ts +++ b/src/tools/mongodb/read/export.ts @@ -81,7 +81,7 @@ export class ExportTool extends MongoDBToolBase { }); } - const exportName = `${database}.${collection}.${new ObjectId().toString()}.json`; + const exportName = `${new ObjectId().toString()}.json`; const { exportURI, exportPath } = await this.session.exportsManager.createJSONExport({ input: cursor, diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 538d8c9bd..8a9a0b9f5 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -8,8 +8,10 @@ import type { Telemetry } from "../telemetry/telemetry.js"; import { type ToolEvent } from "../telemetry/types.js"; import type { UserConfig } from "../common/config.js"; import type { Server } from "../server.js"; +import type { Elicitation } from "../elicitation.js"; export type ToolArgs = z.objectOutputType; +export type ToolCallbackArgs = Parameters>; export type OperationType = "metadata" | "read" | "create" | "delete" | "update" | "connect"; export type ToolCategory = "mongodb" | "atlas"; @@ -18,6 +20,13 @@ export type TelemetryToolMetadata = { orgId?: string; }; +export type ToolConstructorParams = { + session: Session; + config: UserConfig; + telemetry: Telemetry; + elicitation: Elicitation; +}; + export abstract class ToolBase { public abstract name: string; @@ -58,13 +67,35 @@ export abstract class ToolBase { return annotations; } - protected abstract execute(...args: Parameters>): Promise; + protected abstract execute(...args: ToolCallbackArgs): Promise; + + /** Get the confirmation message for the tool. Can be overridden to provide a more specific message. */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + protected getConfirmationMessage(...args: ToolCallbackArgs): string { + return `You are about to execute the \`${this.name}\` tool which requires additional confirmation. Would you like to proceed?`; + } + + /** Check if the user has confirmed the tool execution, if required by the configuration. + * Always returns true if confirmation is not required. + */ + public async verifyConfirmed(args: ToolCallbackArgs): Promise { + if (!this.config.confirmationRequiredTools.includes(this.name)) { + return true; + } + + return this.elicitation.requestConfirmation(this.getConfirmationMessage(...args)); + } - constructor( - protected readonly session: Session, - protected readonly config: UserConfig, - protected readonly telemetry: Telemetry - ) {} + protected readonly session: Session; + protected readonly config: UserConfig; + protected readonly telemetry: Telemetry; + protected readonly elicitation: Elicitation; + constructor({ session, config, telemetry, elicitation }: ToolConstructorParams) { + this.session = session; + this.config = config; + this.telemetry = telemetry; + this.elicitation = elicitation; + } public register(server: Server): boolean { if (!this.verifyAllowed()) { @@ -74,6 +105,22 @@ export abstract class ToolBase { const callback: ToolCallback = async (...args) => { const startTime = Date.now(); try { + if (!(await this.verifyConfirmed(args))) { + this.session.logger.debug({ + id: LogId.toolExecute, + context: "tool", + message: `User did not confirm the execution of the \`${this.name}\` tool so the operation was not performed.`, + noRedaction: true, + }); + return { + content: [ + { + type: "text", + text: `User did not confirm the execution of the \`${this.name}\` tool so the operation was not performed.`, + }, + ], + }; + } this.session.logger.debug({ id: LogId.toolExecute, context: "tool", @@ -82,7 +129,14 @@ export abstract class ToolBase { }); const result = await this.execute(...args); - await this.emitToolEvent(startTime, result, ...args).catch(() => {}); + this.emitToolEvent(startTime, result, ...args); + + this.session.logger.debug({ + id: LogId.toolExecute, + context: "tool", + message: `Executed tool ${this.name}`, + noRedaction: true, + }); return result; } catch (error: unknown) { this.session.logger.error({ @@ -91,7 +145,7 @@ export abstract class ToolBase { message: `Error executing ${this.name}: ${error as string}`, }); const toolResult = await this.handleError(error, args[0] as ToolArgs); - await this.emitToolEvent(startTime, toolResult, ...args).catch(() => {}); + this.emitToolEvent(startTime, toolResult, ...args); return toolResult; } }; @@ -149,7 +203,7 @@ export abstract class ToolBase { let errorClarification: string | undefined; // Check read-only mode first - if (this.config.readOnly && !["read", "metadata"].includes(this.operationType)) { + if (this.config.readOnly && !["read", "metadata", "connect"].includes(this.operationType)) { errorClarification = `read-only mode is enabled, its operation type, \`${this.operationType}\`,`; } else if (this.config.disabledTools.includes(this.category)) { errorClarification = `its category, \`${this.category}\`,`; @@ -200,11 +254,11 @@ export abstract class ToolBase { * @param result - Whether the command succeeded or failed * @param args - The arguments passed to the tool */ - private async emitToolEvent( + private emitToolEvent( startTime: number, result: CallToolResult, ...args: Parameters> - ): Promise { + ): void { if (!this.telemetry.isTelemetryEnabled()) { return; } @@ -230,7 +284,7 @@ export abstract class ToolBase { event.properties.project_id = metadata.projectId; } - await this.telemetry.emitEvents([event]); + this.telemetry.emitEvents([event]); } } diff --git a/src/transports/base.ts b/src/transports/base.ts index 485752e7b..a70d23a2c 100644 --- a/src/transports/base.ts +++ b/src/transports/base.ts @@ -1,4 +1,4 @@ -import type { DriverOptions, UserConfig } from "../common/config.js"; +import type { UserConfig } from "../common/config.js"; import { packageInfo } from "../common/packageInfo.js"; import { Server } from "../server.js"; import { Session } from "../common/session.js"; @@ -7,30 +7,59 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import type { LoggerBase } from "../common/logger.js"; import { CompositeLogger, ConsoleLogger, DiskLogger, McpLogger } from "../common/logger.js"; import { ExportsManager } from "../common/exportsManager.js"; -import { ConnectionManager } from "../common/connectionManager.js"; import { DeviceId } from "../helpers/deviceId.js"; +import { Keychain } from "../common/keychain.js"; +import { createMCPConnectionManager, type ConnectionManagerFactoryFn } from "../common/connectionManager.js"; +import { + type ConnectionErrorHandler, + connectionErrorHandler as defaultConnectionErrorHandler, +} from "../common/connectionErrorHandler.js"; +import type { CommonProperties } from "../telemetry/types.js"; +import { Elicitation } from "../elicitation.js"; + +export type TransportRunnerConfig = { + userConfig: UserConfig; + createConnectionManager?: ConnectionManagerFactoryFn; + connectionErrorHandler?: ConnectionErrorHandler; + additionalLoggers?: LoggerBase[]; + telemetryProperties?: Partial; +}; export abstract class TransportRunnerBase { public logger: LoggerBase; public deviceId: DeviceId; + protected readonly userConfig: UserConfig; + private readonly createConnectionManager: ConnectionManagerFactoryFn; + private readonly connectionErrorHandler: ConnectionErrorHandler; + private readonly telemetryProperties: Partial; - protected constructor( - protected readonly userConfig: UserConfig, - private readonly driverOptions: DriverOptions, - additionalLoggers: LoggerBase[] - ) { + protected constructor({ + userConfig, + createConnectionManager = createMCPConnectionManager, + connectionErrorHandler = defaultConnectionErrorHandler, + additionalLoggers = [], + telemetryProperties = {}, + }: TransportRunnerConfig) { + this.userConfig = userConfig; + this.createConnectionManager = createConnectionManager; + this.connectionErrorHandler = connectionErrorHandler; + this.telemetryProperties = telemetryProperties; const loggers: LoggerBase[] = [...additionalLoggers]; if (this.userConfig.loggers.includes("stderr")) { - loggers.push(new ConsoleLogger()); + loggers.push(new ConsoleLogger(Keychain.root)); } if (this.userConfig.loggers.includes("disk")) { loggers.push( - new DiskLogger(this.userConfig.logPath, (err) => { - // If the disk logger fails to initialize, we log the error to stderr and exit - console.error("Error initializing disk logger:", err); - process.exit(1); - }) + new DiskLogger( + this.userConfig.logPath, + (err) => { + // If the disk logger fails to initialize, we log the error to stderr and exit + console.error("Error initializing disk logger:", err); + process.exit(1); + }, + Keychain.root + ) ); } @@ -38,7 +67,7 @@ export abstract class TransportRunnerBase { this.deviceId = DeviceId.create(this.logger); } - protected setupServer(): Server { + protected async setupServer(): Promise { const mcpServer = new McpServer({ name: packageInfo.mcpServerName, version: packageInfo.version, @@ -46,7 +75,11 @@ export abstract class TransportRunnerBase { const logger = new CompositeLogger(this.logger); const exportsManager = ExportsManager.init(this.userConfig, logger); - const connectionManager = new ConnectionManager(this.userConfig, this.driverOptions, logger, this.deviceId); + const connectionManager = await this.createConnectionManager({ + logger, + userConfig: this.userConfig, + deviceId: this.deviceId, + }); const session = new Session({ apiBaseUrl: this.userConfig.apiBaseUrl, @@ -55,21 +88,28 @@ export abstract class TransportRunnerBase { logger, exportsManager, connectionManager, + keychain: Keychain.root, + }); + + const telemetry = Telemetry.create(session, this.userConfig, this.deviceId, { + commonProperties: this.telemetryProperties, }); - const telemetry = Telemetry.create(session, this.userConfig, this.deviceId); + const elicitation = new Elicitation({ server: mcpServer.server }); const result = new Server({ mcpServer, session, telemetry, userConfig: this.userConfig, + connectionErrorHandler: this.connectionErrorHandler, + elicitation, }); // We need to create the MCP logger after the server is constructed // because it needs the server instance if (this.userConfig.loggers.includes("mcp")) { - logger.addLogger(new McpLogger(result)); + logger.addLogger(new McpLogger(result, Keychain.root)); } return result; diff --git a/src/transports/stdio.ts b/src/transports/stdio.ts index 0751cac7b..09a7490b9 100644 --- a/src/transports/stdio.ts +++ b/src/transports/stdio.ts @@ -1,12 +1,10 @@ -import type { LoggerBase } from "../common/logger.js"; -import { LogId } from "../common/logger.js"; -import type { Server } from "../server.js"; -import { TransportRunnerBase } from "./base.js"; +import { EJSON } from "bson"; import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; import { JSONRPCMessageSchema } from "@modelcontextprotocol/sdk/types.js"; -import { EJSON } from "bson"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import type { DriverOptions, UserConfig } from "../common/config.js"; +import { LogId } from "../common/logger.js"; +import type { Server } from "../server.js"; +import { TransportRunnerBase, type TransportRunnerConfig } from "./base.js"; // This is almost a copy of ReadBuffer from @modelcontextprotocol/sdk // but it uses EJSON.parse instead of JSON.parse to handle BSON types @@ -55,13 +53,13 @@ export function createStdioTransport(): StdioServerTransport { export class StdioRunner extends TransportRunnerBase { private server: Server | undefined; - constructor(userConfig: UserConfig, driverOptions: DriverOptions, additionalLoggers: LoggerBase[] = []) { - super(userConfig, driverOptions, additionalLoggers); + constructor(config: TransportRunnerConfig) { + super(config); } async start(): Promise { try { - this.server = this.setupServer(); + this.server = await this.setupServer(); const transport = createStdioTransport(); diff --git a/src/transports/streamableHttp.ts b/src/transports/streamableHttp.ts index 1718252ca..0a20e59e8 100644 --- a/src/transports/streamableHttp.ts +++ b/src/transports/streamableHttp.ts @@ -1,13 +1,11 @@ import express from "express"; import type http from "http"; +import { randomUUID } from "crypto"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; -import { TransportRunnerBase } from "./base.js"; -import type { DriverOptions, UserConfig } from "../common/config.js"; -import type { LoggerBase } from "../common/logger.js"; import { LogId } from "../common/logger.js"; -import { randomUUID } from "crypto"; import { SessionStore } from "../common/sessionStore.js"; +import { TransportRunnerBase, type TransportRunnerConfig } from "./base.js"; const JSON_RPC_ERROR_CODE_PROCESSING_REQUEST_FAILED = -32000; const JSON_RPC_ERROR_CODE_SESSION_ID_REQUIRED = -32001; @@ -19,6 +17,10 @@ export class StreamableHttpRunner extends TransportRunnerBase { private httpServer: http.Server | undefined; private sessionStore!: SessionStore; + constructor(config: TransportRunnerConfig) { + super(config); + } + public get serverAddress(): string { const result = this.httpServer?.address(); if (typeof result === "string") { @@ -31,10 +33,6 @@ export class StreamableHttpRunner extends TransportRunnerBase { throw new Error("Server is not started yet"); } - constructor(userConfig: UserConfig, driverOptions: DriverOptions, additionalLoggers: LoggerBase[] = []) { - super(userConfig, driverOptions, additionalLoggers); - } - async start(): Promise { const app = express(); this.sessionStore = new SessionStore( @@ -113,7 +111,7 @@ export class StreamableHttpRunner extends TransportRunnerBase { return; } - const server = this.setupServer(); + const server = await this.setupServer(); let keepAliveLoop: NodeJS.Timeout; const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: (): string => randomUUID().toString(), @@ -126,7 +124,7 @@ export class StreamableHttpRunner extends TransportRunnerBase { // eslint-disable-next-line @typescript-eslint/no-misused-promises keepAliveLoop = setInterval(async () => { try { - this.logger.debug({ + server.session.logger.debug({ id: LogId.streamableHttpTransportKeepAlive, context: "streamableHttpTransport", message: "Sending ping", @@ -140,7 +138,7 @@ export class StreamableHttpRunner extends TransportRunnerBase { } catch (err) { try { failedPings++; - this.logger.warning({ + server.session.logger.warning({ id: LogId.streamableHttpTransportKeepAliveFailure, context: "streamableHttpTransport", message: `Error sending ping (attempt #${failedPings}): ${err instanceof Error ? err.message : String(err)}`, @@ -164,7 +162,7 @@ export class StreamableHttpRunner extends TransportRunnerBase { this.logger.error({ id: LogId.streamableHttpTransportSessionCloseFailure, context: "streamableHttpTransport", - message: `Error closing session: ${error instanceof Error ? error.message : String(error)}`, + message: `Error closing session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`, }); } }, @@ -207,6 +205,15 @@ export class StreamableHttpRunner extends TransportRunnerBase { message: `Server started on ${this.serverAddress}`, noRedaction: true, }); + + if (this.shouldWarnAboutHttpHost(this.userConfig.httpHost)) { + this.logger.warning({ + id: LogId.streamableHttpTransportHttpHostWarning, + context: "streamableHttpTransport", + message: `Binding to ${this.userConfig.httpHost} can expose the MCP Server to the entire local network, which allows other devices on the same network to potentially access the MCP Server. This is a security risk and could allow unauthorized access to your database context.`, + noRedaction: true, + }); + } } async closeTransport(): Promise { @@ -245,4 +252,10 @@ export class StreamableHttpRunner extends TransportRunnerBase { }); }; } + + private shouldWarnAboutHttpHost(httpHost: string): boolean { + const host = httpHost.trim(); + const safeHosts = new Set(["127.0.0.1", "localhost", "::1"]); + return host === "0.0.0.0" || host === "::" || (!safeHosts.has(host) && host !== ""); + } } diff --git a/src/types/mongodb-connection-string-url.d.ts b/src/types/mongodb-connection-string-url.d.ts deleted file mode 100644 index 01a0cff2d..000000000 --- a/src/types/mongodb-connection-string-url.d.ts +++ /dev/null @@ -1,69 +0,0 @@ -declare module "mongodb-connection-string-url" { - import { URL } from "whatwg-url"; - import { redactConnectionString, ConnectionStringRedactionOptions } from "./redact"; - export { redactConnectionString, ConnectionStringRedactionOptions }; - declare class CaseInsensitiveMap extends Map { - delete(name: K): boolean; - get(name: K): string | undefined; - has(name: K): boolean; - set(name: K, value: any): this; - _normalizeKey(name: any): K; - } - declare abstract class URLWithoutHost extends URL { - abstract get host(): never; - abstract set host(value: never); - abstract get hostname(): never; - abstract set hostname(value: never); - abstract get port(): never; - abstract set port(value: never); - abstract get href(): string; - abstract set href(value: string); - } - export interface ConnectionStringParsingOptions { - looseValidation?: boolean; - } - export declare class ConnectionString extends URLWithoutHost { - _hosts: string[]; - constructor(uri: string, options?: ConnectionStringParsingOptions); - get host(): never; - set host(_ignored: never); - get hostname(): never; - set hostname(_ignored: never); - get port(): never; - set port(_ignored: never); - get href(): string; - set href(_ignored: string); - get isSRV(): boolean; - get hosts(): string[]; - set hosts(list: string[]); - toString(): string; - clone(): ConnectionString; - redact(options?: ConnectionStringRedactionOptions): ConnectionString; - typedSearchParams(): { - append(name: keyof T & string, value: any): void; - delete(name: keyof T & string): void; - get(name: keyof T & string): string | null; - getAll(name: keyof T & string): string[]; - has(name: keyof T & string): boolean; - set(name: keyof T & string, value: any): void; - keys(): IterableIterator; - values(): IterableIterator; - entries(): IterableIterator<[keyof T & string, string]>; - _normalizeKey(name: keyof T & string): string; - [Symbol.iterator](): IterableIterator<[keyof T & string, string]>; - sort(): void; - forEach( - callback: (this: THIS_ARG, value: string, name: string, searchParams: any) => void, - thisArg?: THIS_ARG | undefined - ): void; - readonly [Symbol.toStringTag]: "URLSearchParams"; - }; - } - export declare class CommaAndColonSeparatedRecord< - K extends {} = Record, - > extends CaseInsensitiveMap { - constructor(from?: string | null); - toString(): string; - } - export default ConnectionString; -} diff --git a/src/types/mongodb-redact.d.ts b/src/types/mongodb-redact.d.ts deleted file mode 100644 index df8801439..000000000 --- a/src/types/mongodb-redact.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module "mongodb-redact" { - function redact(message: T): T; - export default redact; -} diff --git a/tests/integration/build.test.ts b/tests/integration/build.test.ts index f5b26827e..7453cb3d9 100644 --- a/tests/integration/build.test.ts +++ b/tests/integration/build.test.ts @@ -41,13 +41,16 @@ describe("Build Test", () => { const esmKeys = Object.keys(esmModule).sort(); expect(cjsKeys).toEqual(esmKeys); - expect(cjsKeys).toIncludeSameMembers([ - "Server", - "Session", - "Telemetry", - "StreamableHttpRunner", - "defaultUserConfig", - "LoggerBase", - ]); + expect(cjsKeys).toEqual( + expect.arrayContaining([ + "ConnectionManager", + "LoggerBase", + "Server", + "Session", + "StreamableHttpRunner", + "Telemetry", + "defaultUserConfig", + ]) + ); }); }); diff --git a/tests/integration/common/connectionManager.oidc.test.ts b/tests/integration/common/connectionManager.oidc.test.ts index fe65e63d6..2e1741257 100644 --- a/tests/integration/common/connectionManager.oidc.test.ts +++ b/tests/integration/common/connectionManager.oidc.test.ts @@ -5,7 +5,11 @@ import process from "process"; import type { MongoDBIntegrationTestCase } from "../tools/mongodb/mongodbHelpers.js"; import { describeWithMongoDB, isCommunityServer, getServerVersion } from "../tools/mongodb/mongodbHelpers.js"; import { defaultTestConfig, responseAsText, timeout, waitUntil } from "../helpers.js"; -import type { ConnectionStateConnected, ConnectionStateConnecting } from "../../../src/common/connectionManager.js"; +import type { + ConnectionStateConnected, + ConnectionStateConnecting, + TestConnectionManager, +} from "../../../src/common/connectionManager.js"; import type { UserConfig } from "../../../src/common/config.js"; import { setupDriverConfig } from "../../../src/common/config.js"; import path from "path"; @@ -94,7 +98,7 @@ describe.skipIf(process.platform !== "linux")("ConnectionManager OIDC Tests", as ...defaultTestConfig, oidcRedirectURi: "http://localhost:0/", authenticationMechanism: "MONGODB-OIDC", - maxIdleTimeMS: "1", + maxIdleTimeMS: "10000", minPoolSize: "0", username: "testuser", browser: fetchBrowserFixture, @@ -122,7 +126,8 @@ describe.skipIf(process.platform !== "linux")("ConnectionManager OIDC Tests", as } beforeEach(async () => { - const connectionManager = integration.mcpServer().session.connectionManager; + const connectionManager = integration.mcpServer().session + .connectionManager as TestConnectionManager; // disconnect on purpose doesn't change the state if it was failed to avoid losing // information in production. await connectionManager.disconnect(); diff --git a/tests/integration/common/connectionManager.test.ts b/tests/integration/common/connectionManager.test.ts index 5a8cb6dae..9771a1ec2 100644 --- a/tests/integration/common/connectionManager.test.ts +++ b/tests/integration/common/connectionManager.test.ts @@ -2,15 +2,16 @@ import type { ConnectionManagerEvents, ConnectionStateConnected, ConnectionStringAuthType, + TestConnectionManager, } from "../../../src/common/connectionManager.js"; -import { ConnectionManager } from "../../../src/common/connectionManager.js"; +import { MCPConnectionManager } from "../../../src/common/connectionManager.js"; import type { UserConfig } from "../../../src/common/config.js"; import { describeWithMongoDB } from "../tools/mongodb/mongodbHelpers.js"; import { describe, beforeEach, expect, it, vi, afterEach } from "vitest"; describeWithMongoDB("Connection Manager", (integration) => { - function connectionManager(): ConnectionManager { - return integration.mcpServer().session.connectionManager; + function connectionManager(): TestConnectionManager { + return integration.mcpServer().session.connectionManager as TestConnectionManager; } afterEach(async () => { @@ -43,7 +44,7 @@ describeWithMongoDB("Connection Manager", (integration) => { }; for (const [event, spy] of Object.entries(connectionManagerSpies)) { - connectionManager().on(event as keyof ConnectionManagerEvents, spy); + connectionManager().events.on(event as keyof ConnectionManagerEvents, spy); } await connectionManager().connect({ @@ -224,9 +225,12 @@ describe("Connection Manager connection type inference", () => { for (const { userConfig, connectionString, connectionType } of testCases) { it(`infers ${connectionType} from ${connectionString}`, () => { - const actualConnectionType = ConnectionManager.inferConnectionTypeFromSettings(userConfig as UserConfig, { - connectionString, - }); + const actualConnectionType = MCPConnectionManager.inferConnectionTypeFromSettings( + userConfig as UserConfig, + { + connectionString, + } + ); expect(actualConnectionType).toBe(connectionType); }); diff --git a/tests/integration/elicitation.test.ts b/tests/integration/elicitation.test.ts new file mode 100644 index 000000000..0626fd51a --- /dev/null +++ b/tests/integration/elicitation.test.ts @@ -0,0 +1,315 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { describe, it, expect } from "vitest"; +import { defaultDriverOptions, type UserConfig } from "../../src/common/config.js"; +import { defaultTestConfig, setupIntegrationTest } from "./helpers.js"; +import { Elicitation } from "../../src/elicitation.js"; +import { createMockElicitInput } from "../utils/elicitationMocks.js"; + +describe("Elicitation Integration Tests", () => { + function createTestConfig(config: Partial = {}): UserConfig { + return { + ...defaultTestConfig, + telemetry: "disabled", + // Add fake API credentials so Atlas tools get registered + apiClientId: "test-client-id", + apiClientSecret: "test-client-secret", + ...config, + }; + } + + describe("with elicitation support", () => { + const mockElicitInput = createMockElicitInput(); + const integration = setupIntegrationTest( + () => createTestConfig(), + () => defaultDriverOptions, + { elicitInput: mockElicitInput } + ); + + describe("tools requiring confirmation by default", () => { + it("should request confirmation for drop-database tool and proceed when confirmed", async () => { + mockElicitInput.confirmYes(); + + const result = await integration.mcpClient().callTool({ + name: "drop-database", + arguments: { database: "test-db" }, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + expect(mockElicitInput.mock).toHaveBeenCalledWith({ + message: expect.stringContaining("You are about to drop the `test-db` database"), + requestedSchema: Elicitation.CONFIRMATION_SCHEMA, + }); + + // Should attempt to execute (will fail due to no connection, but confirms flow worked) + expect(result.isError).toBe(true); + expect(result.content).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: "text", + text: expect.stringContaining("You need to connect to a MongoDB instance"), + }), + ]) + ); + }); + + it("should not proceed when user declines confirmation", async () => { + mockElicitInput.confirmNo(); + + const result = await integration.mcpClient().callTool({ + name: "drop-database", + arguments: { database: "test-db" }, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + expect(result.isError).toBeFalsy(); + expect(result.content).toEqual([ + { + type: "text", + text: "User did not confirm the execution of the `drop-database` tool so the operation was not performed.", + }, + ]); + }); + + it("should request confirmation for drop-collection tool", async () => { + mockElicitInput.confirmYes(); + + await integration.mcpClient().callTool({ + name: "drop-collection", + arguments: { database: "test-db", collection: "test-collection" }, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + expect(mockElicitInput.mock).toHaveBeenCalledWith({ + message: expect.stringContaining("You are about to drop the `test-collection` collection"), + requestedSchema: expect.objectContaining(Elicitation.CONFIRMATION_SCHEMA), + }); + }); + + it("should request confirmation for delete-many tool", async () => { + mockElicitInput.confirmYes(); + + await integration.mcpClient().callTool({ + name: "delete-many", + arguments: { + database: "test-db", + collection: "test-collection", + filter: { status: "inactive" }, + }, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + expect(mockElicitInput.mock).toHaveBeenCalledWith({ + message: expect.stringContaining("You are about to delete documents"), + requestedSchema: expect.objectContaining(Elicitation.CONFIRMATION_SCHEMA), + }); + }); + + it("should request confirmation for create-db-user tool", async () => { + mockElicitInput.confirmYes(); + + await integration.mcpClient().callTool({ + name: "atlas-create-db-user", + arguments: { + projectId: "507f1f77bcf86cd799439011", // Valid 24-char hex string + username: "test-user", + roles: [{ roleName: "read", databaseName: "test-db" }], + }, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + expect(mockElicitInput.mock).toHaveBeenCalledWith({ + message: expect.stringContaining("You are about to create a database user"), + requestedSchema: expect.objectContaining(Elicitation.CONFIRMATION_SCHEMA), + }); + }); + + it("should request confirmation for create-access-list tool", async () => { + mockElicitInput.confirmYes(); + + await integration.mcpClient().callTool({ + name: "atlas-create-access-list", + arguments: { + projectId: "507f1f77bcf86cd799439011", // Valid 24-char hex string + ipAddresses: ["192.168.1.1"], + }, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + expect(mockElicitInput.mock).toHaveBeenCalledWith({ + message: expect.stringContaining("You are about to add the following entries to the access list"), + requestedSchema: expect.objectContaining(Elicitation.CONFIRMATION_SCHEMA), + }); + }); + }); + + describe("tools not requiring confirmation by default", () => { + it("should not request confirmation for read operations", async () => { + const result = await integration.mcpClient().callTool({ + name: "list-databases", + arguments: {}, + }); + + expect(mockElicitInput.mock).not.toHaveBeenCalled(); + // Should fail with connection error since we're not connected + expect(result.isError).toBe(true); + }); + + it("should not request confirmation for find operations", async () => { + const result = await integration.mcpClient().callTool({ + name: "find", + arguments: { + database: "test-db", + collection: "test-collection", + }, + }); + + expect(mockElicitInput.mock).not.toHaveBeenCalled(); + // Should fail with connection error since we're not connected + expect(result.isError).toBe(true); + }); + }); + }); + + describe("without elicitation support", () => { + const integration = setupIntegrationTest( + () => createTestConfig(), + () => defaultDriverOptions, + { getClientCapabilities: () => ({}) } + ); + + it("should proceed without confirmation for default confirmation-required tools when client lacks elicitation support", async () => { + const result = await integration.mcpClient().callTool({ + name: "drop-database", + arguments: { database: "test-db" }, + }); + + // Note: No mock assertions needed since elicitation is disabled + // Should fail with connection error since we're not connected, but confirms flow bypassed confirmation + expect(result.isError).toBe(true); + expect(result.content).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: "text", + text: expect.stringContaining("You need to connect to a MongoDB instance"), + }), + ]) + ); + }); + }); + + describe("custom confirmation configuration", () => { + const mockElicitInput = createMockElicitInput(); + const integration = setupIntegrationTest( + () => createTestConfig({ confirmationRequiredTools: ["list-databases"] }), + () => defaultDriverOptions, + { elicitInput: mockElicitInput } + ); + + it("should confirm with a generic message with custom configurations for other tools", async () => { + mockElicitInput.confirmYes(); + + await integration.mcpClient().callTool({ + name: "list-databases", + arguments: {}, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + expect(mockElicitInput.mock).toHaveBeenCalledWith({ + message: expect.stringMatching( + /You are about to execute the `list-databases` tool which requires additional confirmation. Would you like to proceed\?/ + ), + requestedSchema: expect.objectContaining(Elicitation.CONFIRMATION_SCHEMA), + }); + }); + + it("should not request confirmation when tool is removed from default confirmationRequiredTools", async () => { + const result = await integration.mcpClient().callTool({ + name: "drop-database", + arguments: { database: "test-db" }, + }); + + expect(mockElicitInput.mock).not.toHaveBeenCalled(); + // Should fail with connection error since we're not connected + expect(result.isError).toBe(true); + }); + }); + + describe("confirmation message content validation", () => { + const mockElicitInput = createMockElicitInput(); + const integration = setupIntegrationTest( + () => createTestConfig(), + () => defaultDriverOptions, + { elicitInput: mockElicitInput } + ); + + it("should include specific details in create-db-user confirmation", async () => { + mockElicitInput.confirmYes(); + + await integration.mcpClient().callTool({ + name: "atlas-create-db-user", + arguments: { + projectId: "507f1f77bcf86cd799439011", // Valid 24-char hex string + username: "myuser", + password: "mypassword", + roles: [ + { roleName: "readWrite", databaseName: "mydb" }, + { roleName: "read", databaseName: "logs", collectionName: "events" }, + ], + clusters: ["cluster1", "cluster2"], + }, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledWith({ + message: expect.stringMatching(/project.*507f1f77bcf86cd799439011/), + requestedSchema: expect.objectContaining(Elicitation.CONFIRMATION_SCHEMA), + }); + }); + + it("should include filter details in delete-many confirmation", async () => { + mockElicitInput.confirmYes(); + + await integration.mcpClient().callTool({ + name: "delete-many", + arguments: { + database: "mydb", + collection: "users", + filter: { status: "inactive", lastLogin: { $lt: "2023-01-01" } }, + }, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledWith({ + message: expect.stringMatching(/mydb.*database/), + requestedSchema: expect.objectContaining(Elicitation.CONFIRMATION_SCHEMA), + }); + }); + }); + + describe("error handling in confirmation flow", () => { + const mockElicitInput = createMockElicitInput(); + const integration = setupIntegrationTest( + () => createTestConfig(), + () => defaultDriverOptions, + { elicitInput: mockElicitInput } + ); + + it("should handle confirmation errors gracefully", async () => { + mockElicitInput.rejectWith(new Error("Confirmation service unavailable")); + + const result = await integration.mcpClient().callTool({ + name: "drop-database", + arguments: { database: "test-db" }, + }); + + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + expect(result.isError).toBe(true); + expect(result.content).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: "text", + text: expect.stringContaining("Error running drop-database"), + }), + ]) + ); + }); + }); +}); diff --git a/tests/integration/helpers.ts b/tests/integration/helpers.ts index b67fbc169..0a2ccfe84 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -10,9 +10,13 @@ import type { UserConfig, DriverOptions } from "../../src/common/config.js"; import { McpError, ResourceUpdatedNotificationSchema } from "@modelcontextprotocol/sdk/types.js"; import { config, driverOptions } from "../../src/common/config.js"; import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest"; -import type { ConnectionState } from "../../src/common/connectionManager.js"; -import { ConnectionManager } from "../../src/common/connectionManager.js"; +import type { ConnectionManager, ConnectionState } from "../../src/common/connectionManager.js"; +import { MCPConnectionManager } from "../../src/common/connectionManager.js"; import { DeviceId } from "../../src/helpers/deviceId.js"; +import { connectionErrorHandler } from "../../src/common/connectionErrorHandler.js"; +import { Keychain } from "../../src/common/keychain.js"; +import { Elicitation } from "../../src/elicitation.js"; +import type { MockClientCapabilities, createMockElicitInput } from "../utils/elicitationMocks.js"; interface ParameterInfo { name: string; @@ -39,7 +43,14 @@ export const defaultDriverOptions: DriverOptions = { export function setupIntegrationTest( getUserConfig: () => UserConfig, - getDriverOptions: () => DriverOptions + getDriverOptions: () => DriverOptions, + { + elicitInput, + getClientCapabilities, + }: { + elicitInput?: ReturnType; + getClientCapabilities?: () => MockClientCapabilities; + } = {} ): IntegrationTest { let mcpClient: Client | undefined; let mcpServer: Server | undefined; @@ -48,6 +59,7 @@ export function setupIntegrationTest( beforeAll(async () => { const userConfig = getUserConfig(); const driverOptions = getDriverOptions(); + const clientCapabilities = getClientCapabilities?.() ?? (elicitInput ? { elicitation: {} } : {}); const clientTransport = new InMemoryTransport(); const serverTransport = new InMemoryTransport(); @@ -65,14 +77,14 @@ export function setupIntegrationTest( version: "1.2.3", }, { - capabilities: {}, + capabilities: clientCapabilities, } ); const exportsManager = ExportsManager.init(userConfig, logger); deviceId = DeviceId.create(logger); - const connectionManager = new ConnectionManager(userConfig, driverOptions, logger, deviceId); + const connectionManager = new MCPConnectionManager(userConfig, driverOptions, logger, deviceId); const session = new Session({ apiBaseUrl: userConfig.apiBaseUrl, @@ -81,10 +93,11 @@ export function setupIntegrationTest( logger, exportsManager, connectionManager, + keychain: new Keychain(), }); // Mock hasValidAccessToken for tests - if (userConfig.apiClientId && userConfig.apiClientSecret) { + if (!userConfig.apiClientId && !userConfig.apiClientSecret) { const mockFn = vi.fn().mockResolvedValue(true); session.apiClient.validateAccessToken = mockFn; } @@ -93,14 +106,25 @@ export function setupIntegrationTest( const telemetry = Telemetry.create(session, userConfig, deviceId); + const mcpServerInstance = new McpServer({ + name: "test-server", + version: "5.2.3", + }); + + // Mock elicitation if provided + if (elicitInput) { + Object.assign(mcpServerInstance.server, { elicitInput: elicitInput.mock }); + } + + const elicitation = new Elicitation({ server: mcpServerInstance.server }); + mcpServer = new Server({ session, userConfig, telemetry, - mcpServer: new McpServer({ - name: "test-server", - version: "5.2.3", - }), + mcpServer: mcpServerInstance, + elicitation, + connectionErrorHandler, }); await mcpServer.connect(serverTransport); @@ -111,6 +135,8 @@ export function setupIntegrationTest( if (mcpServer) { await mcpServer.session.disconnect(); } + + vi.clearAllMocks(); }); afterAll(async () => { diff --git a/tests/integration/resources/exportedData.test.ts b/tests/integration/resources/exportedData.test.ts index df48f515a..6e361bf03 100644 --- a/tests/integration/resources/exportedData.test.ts +++ b/tests/integration/resources/exportedData.test.ts @@ -1,9 +1,9 @@ import path from "path"; import fs from "fs/promises"; -import { Long } from "bson"; +import { EJSON, Long, ObjectId } from "bson"; import { describe, expect, it, beforeEach, afterAll } from "vitest"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { defaultTestConfig, resourceChangedNotification, timeout } from "../helpers.js"; +import { defaultTestConfig, getDataFromUntrustedContent, resourceChangedNotification, timeout } from "../helpers.js"; import { describeWithMongoDB } from "../tools/mongodb/mongodbHelpers.js"; import { contentWithResourceURILink } from "../tools/mongodb/read/export.test.js"; import type { UserConfig } from "../../../src/lib.js"; @@ -18,15 +18,17 @@ const userConfig: UserConfig = { describeWithMongoDB( "exported-data resource", (integration) => { + let docs: { _id: ObjectId; name: string; longNumber?: Long; bigInt?: Long }[]; + let collection: string; + beforeEach(async () => { const mongoClient = integration.mongoClient(); - await mongoClient - .db("db") - .collection("coll") - .insertMany([ - { name: "foo", longNumber: new Long(1234) }, - { name: "bar", bigInt: new Long(123412341234) }, - ]); + collection = new ObjectId().toString(); + docs = [ + { name: "foo", longNumber: new Long(1234), _id: new ObjectId() }, + { name: "bar", bigInt: new Long(123412341234), _id: new ObjectId() }, + ]; + await mongoClient.db("db").collection(collection).insertMany(docs); }); afterAll(async () => { @@ -67,7 +69,7 @@ describeWithMongoDB( name: "export", arguments: { database: "db", - collection: "coll", + collection, exportTitle: "Export for db.coll", exportTarget: [{ name: "find", arguments: {} }], }, @@ -106,7 +108,7 @@ describeWithMongoDB( name: "export", arguments: { database: "db", - collection: "coll", + collection, exportTitle: "Export for db.coll", exportTarget: [{ name: "find", arguments: {} }], }, @@ -125,7 +127,16 @@ describeWithMongoDB( }); expect(response.isError).toBeFalsy(); expect(response.contents[0]?.mimeType).toEqual("application/json"); - expect(response.contents[0]?.text).toContain("foo"); + + expect(response.contents[0]?.text).toContain(`The exported data contains ${docs.length} documents.`); + expect(response.contents[0]?.text).toContain(" { @@ -134,7 +145,7 @@ describeWithMongoDB( name: "export", arguments: { database: "big", - collection: "coll", + collection, exportTitle: "Export for big.coll", exportTarget: [{ name: "find", arguments: {} }], }, @@ -155,10 +166,10 @@ describeWithMongoDB( }, argument: { name: "exportName", - value: "b", + value: "big", }, }); - expect(completeResponse.completion.total).toEqual(1); + expect(completeResponse.completion.total).toBeGreaterThanOrEqual(1); }); }); }, diff --git a/tests/integration/telemetry.test.ts b/tests/integration/telemetry.test.ts index cc51ed8bf..110bb2e5b 100644 --- a/tests/integration/telemetry.test.ts +++ b/tests/integration/telemetry.test.ts @@ -4,8 +4,9 @@ import { config, driverOptions } from "../../src/common/config.js"; import { DeviceId } from "../../src/helpers/deviceId.js"; import { describe, expect, it } from "vitest"; import { CompositeLogger } from "../../src/common/logger.js"; -import { ConnectionManager } from "../../src/common/connectionManager.js"; +import { MCPConnectionManager } from "../../src/common/connectionManager.js"; import { ExportsManager } from "../../src/common/exportsManager.js"; +import { Keychain } from "../../src/common/keychain.js"; describe("Telemetry", () => { it("should resolve the actual device ID", async () => { @@ -19,7 +20,8 @@ describe("Telemetry", () => { apiBaseUrl: "", logger, exportsManager: ExportsManager.init(config, logger), - connectionManager: new ConnectionManager(config, driverOptions, logger, deviceId), + connectionManager: new MCPConnectionManager(config, driverOptions, logger, deviceId), + keychain: new Keychain(), }), config, deviceId diff --git a/tests/integration/tools/atlas/atlasHelpers.ts b/tests/integration/tools/atlas/atlasHelpers.ts index 38a69291d..00ac53feb 100644 --- a/tests/integration/tools/atlas/atlasHelpers.ts +++ b/tests/integration/tools/atlas/atlasHelpers.ts @@ -19,6 +19,7 @@ export function describeWithAtlas(name: string, fn: IntegrationTestFunction): vo ...defaultTestConfig, apiClientId: process.env.MDB_MCP_API_CLIENT_ID, apiClientSecret: process.env.MDB_MCP_API_CLIENT_SECRET, + apiBaseUrl: process.env.MDB_MCP_API_BASE_URL ?? "https://cloud-dev.mongodb.com", }), () => defaultDriverOptions ); @@ -28,6 +29,7 @@ export function describeWithAtlas(name: string, fn: IntegrationTestFunction): vo interface ProjectTestArgs { getProjectId: () => string; + getIpAddress: () => string; } type ProjectTestFunction = (args: ProjectTestArgs) => void; @@ -35,12 +37,22 @@ type ProjectTestFunction = (args: ProjectTestArgs) => void; export function withProject(integration: IntegrationTest, fn: ProjectTestFunction): SuiteCollector { return describe("with project", () => { let projectId: string = ""; + let ipAddress: string = ""; beforeAll(async () => { const apiClient = integration.mcpServer().session.apiClient; + // check that it has credentials + if (!apiClient.hasCredentials()) { + throw new Error("No credentials available"); + } + + // validate access token + await apiClient.validateAccessToken(); try { const group = await createProject(apiClient); + const ipInfo = await apiClient.getIpInfo(); + ipAddress = ipInfo.currentIpv4Address; projectId = group.id; } catch (error) { console.error("Failed to create project:", error); @@ -64,6 +76,7 @@ export function withProject(integration: IntegrationTest, fn: ProjectTestFunctio const args = { getProjectId: (): string => projectId, + getIpAddress: (): string => ipAddress, }; fn(args); @@ -111,5 +124,22 @@ async function createProject(apiClient: ApiClient): Promise>; } diff --git a/tests/integration/tools/atlas/clusters.test.ts b/tests/integration/tools/atlas/clusters.test.ts index 9ae7aabcb..5c50c570c 100644 --- a/tests/integration/tools/atlas/clusters.test.ts +++ b/tests/integration/tools/atlas/clusters.test.ts @@ -1,6 +1,6 @@ import type { Session } from "../../../../src/common/session.js"; -import { expectDefined, getResponseElements } from "../../helpers.js"; -import { describeWithAtlas, withProject, randomId } from "./atlasHelpers.js"; +import { expectDefined, getDataFromUntrustedContent, getResponseElements } from "../../helpers.js"; +import { describeWithAtlas, withProject, randomId, parseTable } from "./atlasHelpers.js"; import type { ClusterDescription20240805 } from "../../../../src/common/atlas/openapi.js"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; @@ -57,7 +57,7 @@ async function waitCluster( } describeWithAtlas("clusters", (integration) => { - withProject(integration, ({ getProjectId }) => { + withProject(integration, ({ getProjectId, getIpAddress }) => { const clusterName = "ClusterTest-" + randomId; afterAll(async () => { @@ -84,7 +84,6 @@ describeWithAtlas("clusters", (integration) => { it("should create a free cluster and add current IP to access list", async () => { const projectId = getProjectId(); const session = integration.mcpServer().session; - const ipInfo = await session.apiClient.getIpInfo(); const response = await integration.mcpClient().callTool({ name: "atlas-create-free-cluster", @@ -102,7 +101,7 @@ describeWithAtlas("clusters", (integration) => { const accessList = await session.apiClient.listProjectIpAccessLists({ params: { path: { groupId: projectId } }, }); - const found = accessList.results?.some((entry) => entry.ipAddress === ipInfo.currentIpv4Address); + const found = accessList.results?.some((entry) => entry.ipAddress === getIpAddress()); expect(found).toBe(true); }); }); @@ -153,15 +152,19 @@ describeWithAtlas("clusters", (integration) => { const elements = getResponseElements(response); expect(elements).toHaveLength(2); - expect(elements[0]?.text).toMatch(/Found \d+ clusters in project/); + expect(elements[1]?.text).toContain(" { beforeAll(async () => { const projectId = getProjectId(); + const ipAddress = getIpAddress(); await waitCluster(integration.mcpServer().session, projectId, clusterName, (cluster) => { return ( cluster.stateName === "IDLE" && @@ -177,7 +180,7 @@ describeWithAtlas("clusters", (integration) => { body: [ { comment: "MCP test", - cidrBlock: "0.0.0.0/0", + ipAddress: ipAddress, }, ], }); @@ -196,6 +199,7 @@ describeWithAtlas("clusters", (integration) => { it("connects to cluster", async () => { const projectId = getProjectId(); + let connected = false; for (let i = 0; i < 10; i++) { const response = await integration.mcpClient().callTool({ @@ -205,16 +209,25 @@ describeWithAtlas("clusters", (integration) => { const elements = getResponseElements(response.content); expect(elements.length).toBeGreaterThanOrEqual(1); - if ( - elements[0]?.text.includes("Cluster is already connected.") || - elements[0]?.text.includes(`Connected to cluster "${clusterName}"`) - ) { - break; // success + if (elements[0]?.text.includes(`Connected to cluster "${clusterName}"`)) { + connected = true; + + // assert that some of the element s have the message + expect( + elements.some((element) => + element.text.includes( + "Note: A temporary user has been created to enable secure connection to the cluster. For more information, see https://dochub.mongodb.org/core/mongodb-mcp-server-tools-considerations" + ) + ) + ).toBe(true); + + break; } else { expect(elements[0]?.text).toContain(`Attempting to connect to cluster "${clusterName}"...`); } await sleep(500); } + expect(connected).toBe(true); }); describe("when not connected", () => { diff --git a/tests/integration/tools/atlas/dbUsers.test.ts b/tests/integration/tools/atlas/dbUsers.test.ts index b6b055321..fee08b421 100644 --- a/tests/integration/tools/atlas/dbUsers.test.ts +++ b/tests/integration/tools/atlas/dbUsers.test.ts @@ -2,6 +2,7 @@ import { describeWithAtlas, withProject, randomId } from "./atlasHelpers.js"; import { expectDefined, getResponseElements } from "../../helpers.js"; import { ApiClientError } from "../../../../src/common/atlas/apiClientError.js"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { Keychain } from "../../../../src/common/keychain.js"; describeWithAtlas("db users", (integration) => { withProject(integration, ({ getProjectId }) => { @@ -53,6 +54,14 @@ describeWithAtlas("db users", (integration) => { }); describe("atlas-create-db-user", () => { + beforeEach(() => { + Keychain.root.clearAllSecrets(); + }); + + afterEach(() => { + Keychain.root.clearAllSecrets(); + }); + it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); const createDbUser = tools.find((tool) => tool.name === "atlas-create-db-user"); @@ -74,6 +83,16 @@ describeWithAtlas("db users", (integration) => { expect(elements[0]?.text).toContain("created successfully"); expect(elements[0]?.text).toContain(userName); expect(elements[0]?.text).not.toContain("testpassword"); + + expect(integration.mcpServer().session.keychain.allSecrets).toContainEqual({ + value: userName, + kind: "user", + }); + + expect(integration.mcpServer().session.keychain.allSecrets).toContainEqual({ + value: "testpassword", + kind: "password", + }); }); it("should create a database user with generated password", async () => { @@ -83,6 +102,24 @@ describeWithAtlas("db users", (integration) => { expect(elements[0]?.text).toContain("created successfully"); expect(elements[0]?.text).toContain(userName); expect(elements[0]?.text).toContain("with password: `"); + + const passwordStart = elements[0]?.text.lastIndexOf(":") ?? -1; + const passwordEnd = elements[0]?.text.length ?? 1 - 1; + + const password = elements[0]?.text + .substring(passwordStart + 1, passwordEnd - 1) + .replace(/`/g, "") + .trim(); + + expect(integration.mcpServer().session.keychain.allSecrets).toContainEqual({ + value: userName, + kind: "user", + }); + + expect(integration.mcpServer().session.keychain.allSecrets).toContainEqual({ + value: password, + kind: "password", + }); }); it("should add current IP to access list when creating a database user", async () => { diff --git a/tests/integration/tools/atlas/projects.test.ts b/tests/integration/tools/atlas/projects.test.ts index 631b00f87..de637a23a 100644 --- a/tests/integration/tools/atlas/projects.test.ts +++ b/tests/integration/tools/atlas/projects.test.ts @@ -61,7 +61,6 @@ describeWithAtlas("projects", (integration) => { const response = await integration.mcpClient().callTool({ name: "atlas-list-projects", arguments: {} }); const elements = getResponseElements(response); expect(elements).toHaveLength(2); - expect(elements[0]?.text).toMatch(/Found \d+ projects/); expect(elements[1]?.text).toContain(" { } } expect(found).toBe(true); + + expect(elements[0]?.text).toBe(`Found ${data.length} projects`); }); }); }); diff --git a/tests/integration/tools/mongodb/metadata/logs.test.ts b/tests/integration/tools/mongodb/metadata/logs.test.ts index 27c1f0e28..aa7e4ea44 100644 --- a/tests/integration/tools/mongodb/metadata/logs.test.ts +++ b/tests/integration/tools/mongodb/metadata/logs.test.ts @@ -1,5 +1,10 @@ import { expect, it } from "vitest"; -import { validateToolMetadata, validateThrowsForInvalidArguments, getResponseElements } from "../../../helpers.js"; +import { + validateToolMetadata, + validateThrowsForInvalidArguments, + getResponseElements, + getDataFromUntrustedContent, +} from "../../../helpers.js"; import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js"; describeWithMongoDB("logs tool", (integration) => { @@ -36,13 +41,27 @@ describeWithMongoDB("logs tool", (integration) => { const elements = getResponseElements(response); + expect(elements).toHaveLength(2); + expect(elements[1]?.text).toContain(" logs.length) { + expect(elements[0]?.text).toContain(`(showing only the first ${logs.length})`); + } - for (let i = 1; i < elements.length; i++) { + for (const message of logs) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const log = JSON.parse(elements[i]?.text ?? "{}"); + const log = JSON.parse(message ?? "{}"); expect(log).toHaveProperty("t"); expect(log).toHaveProperty("msg"); } @@ -58,9 +77,18 @@ describeWithMongoDB("logs tool", (integration) => { }); const elements = getResponseElements(response); - expect(elements.length).toBeLessThanOrEqual(51); - for (let i = 1; i < elements.length; i++) { - const log = JSON.parse(elements[i]?.text ?? "{}") as { tags: string[] }; + expect(elements).toHaveLength(2); + expect(elements[1]?.text).toContain(" { + switch (error.code) { + case ErrorCodes.NotConnectedToMongoDB: + return { + errorHandled: true, + result: { + isError: true, + content: [ + { + type: "text", + text: "Custom handler - Not connected", + }, + ], + }, + }; + case ErrorCodes.MisconfiguredConnectionString: + return { + errorHandled: true, + result: { + isError: true, + content: [ + { + type: "text", + text: "Custom handler - Misconfigured", + }, + ], + }, + }; + } +}; + +describe("MongoDBTool implementations", () => { + const mdbIntegration = setupMongoDBIntegrationTest({ enterprise: false }, []); + const executeStub: MockedFunction<() => Promise> = vi + .fn() + .mockResolvedValue({ content: [{ type: "text", text: "Something" }] }); + class RandomTool extends MongoDBToolBase { + name = "Random"; + operationType: OperationType = "read"; + protected description = "This is a tool."; + protected argsShape = {}; + public async execute(): Promise { + await this.ensureConnected(); + return executeStub(); + } + } + + let tool: RandomTool | undefined; + let mcpClient: Client | undefined; + let mcpServer: Server | undefined; + let deviceId: DeviceId | undefined; + + async function cleanupAndStartServer( + config: Partial | undefined = {}, + errorHandler: ConnectionErrorHandler | undefined = connectionErrorHandler + ): Promise { + await cleanup(); + const userConfig: UserConfig = { ...defaultTestConfig, telemetry: "disabled", ...config }; + const driverOptions = defaultDriverOptions; + const logger = new CompositeLogger(); + const exportsManager = ExportsManager.init(userConfig, logger); + deviceId = DeviceId.create(logger); + const connectionManager = new MCPConnectionManager(userConfig, driverOptions, logger, deviceId); + const session = new Session({ + apiBaseUrl: userConfig.apiBaseUrl, + apiClientId: userConfig.apiClientId, + apiClientSecret: userConfig.apiClientSecret, + logger, + exportsManager, + connectionManager, + keychain: new Keychain(), + }); + const telemetry = Telemetry.create(session, userConfig, deviceId); + + const clientTransport = new InMemoryTransport(); + const serverTransport = new InMemoryTransport(); + + await serverTransport.start(); + await clientTransport.start(); + + void clientTransport.output.pipeTo(serverTransport.input); + void serverTransport.output.pipeTo(clientTransport.input); + + mcpClient = new Client( + { + name: "test-client", + version: "1.2.3", + }, + { + capabilities: {}, + } + ); + + const internalMcpServer = new McpServer({ + name: "test-server", + version: "5.2.3", + }); + const elicitation = new Elicitation({ server: internalMcpServer.server }); + + mcpServer = new Server({ + session, + userConfig, + telemetry, + mcpServer: internalMcpServer, + connectionErrorHandler: errorHandler, + elicitation, + }); + + tool = new RandomTool({ + session, + config: userConfig, + telemetry, + elicitation, + }); + tool.register(mcpServer); + + await mcpServer.connect(serverTransport); + await mcpClient.connect(clientTransport); + } + + async function cleanup(): Promise { + await mcpServer?.session.disconnect(); + await mcpClient?.close(); + mcpClient = undefined; + + await mcpServer?.close(); + mcpServer = undefined; + + deviceId?.close(); + deviceId = undefined; + + tool = undefined; + } + + beforeEach(async () => { + await cleanupAndStartServer(); + }); + + afterEach(async () => { + vi.clearAllMocks(); + if (mcpServer) { + await mcpServer.session.disconnect(); + } + }); + + afterAll(cleanup); + + describe("when MCP is using default connection error handler", () => { + describe("and comes across a MongoDB Error - NotConnectedToMongoDB", () => { + it("should handle the error", async () => { + const toolResponse = await mcpClient?.callTool({ + name: "Random", + arguments: {}, + }); + expect(toolResponse?.isError).to.equal(true); + expect(toolResponse?.content).toEqual( + expect.arrayContaining([ + { + type: "text", + text: "You need to connect to a MongoDB instance before you can access its data.", + }, + ]) + ); + }); + }); + + describe("and comes across a MongoDB Error - MisconfiguredConnectionString", () => { + it("should handle the error", async () => { + // This is a misconfigured connection string + await cleanupAndStartServer({ connectionString: "mongodb://localhost:1234" }); + const toolResponse = await mcpClient?.callTool({ + name: "Random", + arguments: {}, + }); + expect(toolResponse?.isError).to.equal(true); + expect(toolResponse?.content).toEqual( + expect.arrayContaining([ + { + type: "text", + text: "The configured connection string is not valid. Please check the connection string and confirm it points to a valid MongoDB instance.", + }, + ]) + ); + }); + }); + + describe("and comes across any other error MongoDB Error - ForbiddenCollscan", () => { + it("should not handle the error and let the static handling take over it", async () => { + // This is a misconfigured connection string + await cleanupAndStartServer({ connectionString: mdbIntegration.connectionString(), indexCheck: true }); + const toolResponse = await mcpClient?.callTool({ + name: "find", + arguments: { + database: "db1", + collection: "coll1", + }, + }); + expect(toolResponse?.isError).to.equal(true); + expect(toolResponse?.content).toEqual( + expect.arrayContaining([ + { + type: "text", + text: "Index check failed: The find operation on \"db1.coll1\" performs a collection scan (COLLSCAN) instead of using an index. Consider adding an index for better performance. Use 'explain' tool for query plan analysis or 'collection-indexes' to view existing indexes. To disable this check, set MDB_MCP_INDEX_CHECK to false.", + }, + ]) + ); + }); + }); + }); + + describe("when MCP is using injected connection error handler", () => { + beforeEach(async () => { + await cleanupAndStartServer(defaultTestConfig, injectedErrorHandler); + }); + + describe("and comes across a MongoDB Error - NotConnectedToMongoDB", () => { + it("should handle the error", async () => { + const toolResponse = await mcpClient?.callTool({ + name: "Random", + arguments: {}, + }); + expect(toolResponse?.isError).to.equal(true); + expect(toolResponse?.content).toEqual( + expect.arrayContaining([ + { + type: "text", + text: "Custom handler - Not connected", + }, + ]) + ); + }); + }); + + describe("and comes across a MongoDB Error - MisconfiguredConnectionString", () => { + it("should handle the error", async () => { + // This is a misconfigured connection string + await cleanupAndStartServer({ connectionString: "mongodb://localhost:1234" }, injectedErrorHandler); + const toolResponse = await mcpClient?.callTool({ + name: "Random", + arguments: {}, + }); + expect(toolResponse?.isError).to.equal(true); + expect(toolResponse?.content).toEqual( + expect.arrayContaining([ + { + type: "text", + text: "Custom handler - Misconfigured", + }, + ]) + ); + }); + }); + + describe("and comes across any other error MongoDB Error - ForbiddenCollscan", () => { + it("should not handle the error and let the static handling take over it", async () => { + // This is a misconfigured connection string + await cleanupAndStartServer( + { connectionString: mdbIntegration.connectionString(), indexCheck: true }, + injectedErrorHandler + ); + const toolResponse = await mcpClient?.callTool({ + name: "find", + arguments: { + database: "db1", + collection: "coll1", + }, + }); + expect(toolResponse?.isError).to.equal(true); + expect(toolResponse?.content).toEqual( + expect.arrayContaining([ + { + type: "text", + text: "Index check failed: The find operation on \"db1.coll1\" performs a collection scan (COLLSCAN) instead of using an index. Consider adding an index for better performance. Use 'explain' tool for query plan analysis or 'collection-indexes' to view existing indexes. To disable this check, set MDB_MCP_INDEX_CHECK to false.", + }, + ]) + ); + }); + }); + }); +}); diff --git a/tests/integration/tools/mongodb/read/aggregate.test.ts b/tests/integration/tools/mongodb/read/aggregate.test.ts index fbe72ae80..57c7f8c70 100644 --- a/tests/integration/tools/mongodb/read/aggregate.test.ts +++ b/tests/integration/tools/mongodb/read/aggregate.test.ts @@ -4,10 +4,15 @@ import { validateThrowsForInvalidArguments, getResponseContent, } from "../../../helpers.js"; -import { expect, it } from "vitest"; +import { expect, it, afterEach } from "vitest"; import { describeWithMongoDB, getDocsFromUntrustedContent, validateAutoConnectBehavior } from "../mongodbHelpers.js"; describeWithMongoDB("aggregate tool", (integration) => { + afterEach(() => { + integration.mcpServer().userConfig.readOnly = false; + integration.mcpServer().userConfig.disabledTools = []; + }); + validateToolMetadata(integration, "aggregate", "Run an aggregation against a MongoDB collection", [ ...databaseCollectionParameters, { @@ -95,6 +100,76 @@ describeWithMongoDB("aggregate tool", (integration) => { ); }); + it("can not run $out stages in readOnly mode", async () => { + await integration.connectMcpClient(); + integration.mcpServer().userConfig.readOnly = true; + const response = await integration.mcpClient().callTool({ + name: "aggregate", + arguments: { + database: integration.randomDbName(), + collection: "people", + pipeline: [{ $out: "outpeople" }], + }, + }); + const content = getResponseContent(response); + expect(content).toEqual( + "Error running aggregate: In readOnly mode you can not run pipelines with $out or $merge stages." + ); + }); + + it("can not run $merge stages in readOnly mode", async () => { + await integration.connectMcpClient(); + integration.mcpServer().userConfig.readOnly = true; + const response = await integration.mcpClient().callTool({ + name: "aggregate", + arguments: { + database: integration.randomDbName(), + collection: "people", + pipeline: [{ $merge: "outpeople" }], + }, + }); + const content = getResponseContent(response); + expect(content).toEqual( + "Error running aggregate: In readOnly mode you can not run pipelines with $out or $merge stages." + ); + }); + + for (const disabledOpType of ["create", "update", "delete"] as const) { + it(`can not run $out stages when ${disabledOpType} operation is disabled`, async () => { + await integration.connectMcpClient(); + integration.mcpServer().userConfig.disabledTools = [disabledOpType]; + const response = await integration.mcpClient().callTool({ + name: "aggregate", + arguments: { + database: integration.randomDbName(), + collection: "people", + pipeline: [{ $out: "outpeople" }], + }, + }); + const content = getResponseContent(response); + expect(content).toEqual( + "Error running aggregate: When 'create', 'update', or 'delete' operations are disabled, you can not run pipelines with $out or $merge stages." + ); + }); + + it(`can not run $merge stages when ${disabledOpType} operation is disabled`, async () => { + await integration.connectMcpClient(); + integration.mcpServer().userConfig.disabledTools = [disabledOpType]; + const response = await integration.mcpClient().callTool({ + name: "aggregate", + arguments: { + database: integration.randomDbName(), + collection: "people", + pipeline: [{ $merge: "outpeople" }], + }, + }); + const content = getResponseContent(response); + expect(content).toEqual( + "Error running aggregate: When 'create', 'update', or 'delete' operations are disabled, you can not run pipelines with $out or $merge stages." + ); + }); + } + validateAutoConnectBehavior(integration, "aggregate", () => { return { args: { diff --git a/tests/integration/transports/streamableHttp.test.ts b/tests/integration/transports/streamableHttp.test.ts index f45ce3cd3..7f57135d8 100644 --- a/tests/integration/transports/streamableHttp.test.ts +++ b/tests/integration/transports/streamableHttp.test.ts @@ -1,10 +1,12 @@ import { StreamableHttpRunner } from "../../../src/transports/streamableHttp.js"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; -import { describe, expect, it, beforeAll, afterAll, beforeEach } from "vitest"; -import { config, driverOptions } from "../../../src/common/config.js"; +import { describe, expect, it, beforeAll, afterAll, beforeEach, afterEach } from "vitest"; +import { config } from "../../../src/common/config.js"; import type { LoggerType, LogLevel, LogPayload } from "../../../src/common/logger.js"; import { LoggerBase, LogId } from "../../../src/common/logger.js"; +import { createMCPConnectionManager } from "../../../src/common/connectionManager.js"; +import { Keychain } from "../../../src/common/keychain.js"; describe("StreamableHttpRunner", () => { let runner: StreamableHttpRunner; @@ -28,7 +30,7 @@ describe("StreamableHttpRunner", () => { describe(description, () => { beforeAll(async () => { config.httpHeaders = headers; - runner = new StreamableHttpRunner(config, driverOptions); + runner = new StreamableHttpRunner({ userConfig: config }); await runner.start(); }); @@ -109,7 +111,7 @@ describe("StreamableHttpRunner", () => { try { for (let i = 0; i < 3; i++) { config.httpPort = 0; // Use a random port for each runner - const runner = new StreamableHttpRunner(config, driverOptions); + const runner = new StreamableHttpRunner({ userConfig: config }); await runner.start(); runners.push(runner); } @@ -137,8 +139,12 @@ describe("StreamableHttpRunner", () => { } it("can provide custom logger", async () => { - const logger = new CustomLogger(); - const runner = new StreamableHttpRunner(config, driverOptions, [logger]); + const logger = new CustomLogger(new Keychain()); + const runner = new StreamableHttpRunner({ + userConfig: config, + createConnectionManager: createMCPConnectionManager, + additionalLoggers: [logger], + }); await runner.start(); const messages = logger.messages; @@ -153,4 +159,26 @@ describe("StreamableHttpRunner", () => { expect(serverStartedMessage?.level).toBe("info"); }); }); + + describe("with telemetry properties", () => { + afterEach(async () => { + await runner.close(); + config.telemetry = oldTelemetry; + config.loggers = oldLoggers; + config.httpHeaders = {}; + }); + + it("merges them with the base properties", async () => { + config.telemetry = "enabled"; + runner = new StreamableHttpRunner({ + userConfig: config, + telemetryProperties: { hosting_mode: "vscode-extension" }, + }); + await runner.start(); + + const server = await runner["setupServer"](); + const properties = server["telemetry"].getCommonProperties(); + expect(properties.hosting_mode).toBe("vscode-extension"); + }); + }); }); diff --git a/tests/unit/args.test.ts b/tests/unit/args.test.ts new file mode 100644 index 000000000..d7a5a1eb4 --- /dev/null +++ b/tests/unit/args.test.ts @@ -0,0 +1,397 @@ +import { describe, expect, it } from "vitest"; +import { + AtlasArgs, + CommonArgs, + ALLOWED_PROJECT_NAME_CHARACTERS_ERROR, + ALLOWED_USERNAME_CHARACTERS_ERROR, + ALLOWED_REGION_CHARACTERS_ERROR, + ALLOWED_CLUSTER_NAME_CHARACTERS_ERROR, + NO_UNICODE_ERROR, +} from "../../src/tools/args.js"; + +describe("Tool args", () => { + describe("CommonArgs", () => { + describe("string", () => { + it("should return a ZodString schema", () => { + const schema = CommonArgs.string(); + expect(schema).toBeDefined(); + expect(schema.parse("test")).toBe("test"); + }); + + it("should accept any string value", () => { + const schema = CommonArgs.string(); + expect(schema.parse("hello")).toBe("hello"); + expect(schema.parse("123")).toBe("123"); + expect(schema.parse("test@#$%")).toBe("test@#$%"); + }); + + it("should not allow special characters and unicode symbols", () => { + const schema = CommonArgs.string(); + + // Unicode characters + expect(() => schema.parse("héllo")).toThrow(NO_UNICODE_ERROR); + expect(() => schema.parse("测试")).toThrow(NO_UNICODE_ERROR); + expect(() => schema.parse("café")).toThrow(NO_UNICODE_ERROR); + + // Emojis + expect(() => schema.parse("🚀")).toThrow(NO_UNICODE_ERROR); + expect(() => schema.parse("hello😀")).toThrow(NO_UNICODE_ERROR); + + // Control characters (below ASCII 32) + expect(() => schema.parse("hello\nworld")).toThrow(NO_UNICODE_ERROR); + expect(() => schema.parse("hello\tworld")).toThrow(NO_UNICODE_ERROR); + expect(() => schema.parse("hello\0world")).toThrow(NO_UNICODE_ERROR); + + // Extended ASCII characters (above ASCII 126) + expect(() => schema.parse("hello\x80")).toThrow(NO_UNICODE_ERROR); + expect(() => schema.parse("hello\xFF")).toThrow(NO_UNICODE_ERROR); + }); + + it("should reject non-string values", () => { + const schema = CommonArgs.string(); + expect(() => schema.parse(123)).toThrow(); + expect(() => schema.parse(null)).toThrow(); + expect(() => schema.parse(undefined)).toThrow(); + expect(() => schema.parse({})).toThrow(); + }); + }); + + describe("objectId", () => { + it("should validate 24-character hexadecimal strings", () => { + const schema = CommonArgs.objectId("Test ID"); + const validId = "507f1f77bcf86cd799439011"; + expect(schema.parse(validId)).toBe(validId); + }); + + it("should reject invalid ObjectId formats", () => { + const schema = CommonArgs.objectId("Test ID"); + + // Too short + expect(() => schema.parse("507f1f77bcf86cd79943901")).toThrow(); + + // Too long + expect(() => schema.parse("507f1f77bcf86cd7994390111")).toThrow(); + + // Invalid characters + expect(() => schema.parse("507f1f77bcf86cd79943901g")).toThrow(); + expect(() => schema.parse("507f1f77bcf86cd79943901!")).toThrow(); + + // Empty string + expect(() => schema.parse("")).toThrow(); + }); + + it("should provide custom field name in error messages", () => { + const schema = CommonArgs.objectId("Custom Field"); + expect(() => schema.parse("invalid")).toThrow("Custom Field must be exactly 24 characters"); + }); + + it("should not fail if the value is optional", () => { + const schema = CommonArgs.objectId("Custom Field").optional(); + expect(schema.parse(undefined)).toBeUndefined(); + }); + + it("should not fail if the value is empty", () => { + const schema = CommonArgs.objectId("Custom Field"); + expect(() => schema.parse(undefined)).toThrow("Required"); + }); + }); + }); + + describe("AtlasArgs", () => { + describe("projectId", () => { + it("should validate project IDs", () => { + const schema = AtlasArgs.projectId(); + const validId = "507f1f77bcf86cd799439011"; + expect(schema.parse(validId)).toBe(validId); + }); + + it("should reject invalid project IDs", () => { + const schema = AtlasArgs.projectId(); + expect(() => schema.parse("invalid")).toThrow("projectId must be exactly 24 characters"); + expect(() => schema.parse("507f1f77bc*86cd79943901")).toThrow( + "projectId must contain only hexadecimal characters" + ); + expect(() => schema.parse("")).toThrow("projectId is required"); + expect(() => schema.parse("507f1f77/bcf86cd799439011")).toThrow( + "projectId must contain only hexadecimal characters" + ); + }); + }); + + describe("organizationId", () => { + it("should validate organization IDs", () => { + const schema = AtlasArgs.organizationId(); + const validId = "507f1f77bcf86cd799439011"; + expect(schema.parse(validId)).toBe(validId); + }); + + it("should reject invalid organization IDs", () => { + const schema = AtlasArgs.organizationId(); + expect(() => schema.parse("invalid")).toThrow("organizationId must be exactly 24 characters"); + }); + }); + + describe("clusterName", () => { + it("should validate valid cluster names", () => { + const schema = AtlasArgs.clusterName(); + const validNames = ["my-cluster", "cluster_1", "Cluster123", "test-cluster-2", "my_cluster_name"]; + + validNames.forEach((name) => { + expect(schema.parse(name)).toBe(name); + }); + }); + + it("should reject invalid cluster names", () => { + const schema = AtlasArgs.clusterName(); + + // Empty string + expect(() => schema.parse("")).toThrow("Cluster name is required"); + + // Too long (over 64 characters) + const longName = "a".repeat(65); + expect(() => schema.parse(longName)).toThrow("Cluster name must be 64 characters or less"); + + // Invalid characters + expect(() => schema.parse("cluster@name")).toThrow(ALLOWED_CLUSTER_NAME_CHARACTERS_ERROR); + expect(() => schema.parse("cluster name")).toThrow(ALLOWED_CLUSTER_NAME_CHARACTERS_ERROR); + expect(() => schema.parse("cluster.name")).toThrow(ALLOWED_CLUSTER_NAME_CHARACTERS_ERROR); + expect(() => schema.parse("cluster/name")).toThrow(ALLOWED_CLUSTER_NAME_CHARACTERS_ERROR); + }); + + it("should accept exactly 64 characters", () => { + const schema = AtlasArgs.clusterName(); + const maxLengthName = "a".repeat(64); + expect(schema.parse(maxLengthName)).toBe(maxLengthName); + }); + }); + + describe("username", () => { + it("should validate valid usernames", () => { + const schema = AtlasArgs.username(); + const validUsernames = ["user123", "user_name", "user.name", "user-name", "User123", "test.user_name"]; + + validUsernames.forEach((username) => { + expect(schema.parse(username)).toBe(username); + }); + }); + + it("should reject invalid usernames", () => { + const schema = AtlasArgs.username(); + + // Empty string + expect(() => schema.parse("")).toThrow("Username is required"); + + // Too long (over 100 characters) + const longUsername = "a".repeat(101); + expect(() => schema.parse(longUsername)).toThrow("Username must be 100 characters or less"); + + // Invalid characters + expect(() => schema.parse("user@name")).toThrow(ALLOWED_USERNAME_CHARACTERS_ERROR); + expect(() => schema.parse("user name")).toThrow(ALLOWED_USERNAME_CHARACTERS_ERROR); + }); + + it("should accept exactly 100 characters", () => { + const schema = AtlasArgs.username(); + const maxLengthUsername = "a".repeat(100); + expect(schema.parse(maxLengthUsername)).toBe(maxLengthUsername); + }); + }); + + describe("ipAddress", () => { + it("should validate valid IPv4 addresses", () => { + const schema = AtlasArgs.ipAddress(); + const validIPs = ["192.168.1.1", "10.0.0.1", "172.16.0.1", "127.0.0.1", "0.0.0.0", "255.255.255.255"]; + + validIPs.forEach((ip) => { + expect(schema.parse(ip)).toBe(ip); + }); + }); + + it("should reject invalid IP addresses", () => { + const schema = AtlasArgs.ipAddress(); + + // Invalid formats + expect(() => schema.parse("192.168.1")).toThrow(); + expect(() => schema.parse("192.168.1.1.1")).toThrow(); + expect(() => schema.parse("192.168.1.256")).toThrow(); + expect(() => schema.parse("192.168.1.-1")).toThrow(); + expect(() => schema.parse("not-an-ip")).toThrow(); + + // IPv6 (should be rejected since we only support IPv4) + expect(() => schema.parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toThrow(); + }); + }); + + describe("cidrBlock", () => { + it("should validate valid CIDR blocks", () => { + const schema = AtlasArgs.cidrBlock(); + const validCIDRs = ["192.168.1.0/24", "10.0.0.0/8", "172.16.0.0/12", "0.0.0.0/0", "192.168.1.1/32"]; + + validCIDRs.forEach((cidr) => { + expect(schema.parse(cidr)).toBe(cidr); + }); + }); + + it("should reject invalid CIDR blocks", () => { + const schema = AtlasArgs.cidrBlock(); + + // Invalid formats + expect(() => schema.parse("192.168.1.0")).toThrow("Invalid cidr"); + expect(() => schema.parse("192.168.1.0/")).toThrow("Invalid cidr"); + expect(() => schema.parse("192.168.1.0/33")).toThrow("Invalid cidr"); + expect(() => schema.parse("192.168.1.256/24")).toThrow("Invalid cidr"); + expect(() => schema.parse("not-a-cidr")).toThrow("Invalid cidr"); + }); + }); + + describe("region", () => { + it("should validate valid region names", () => { + const schema = AtlasArgs.region(); + const validRegions = [ + "US_EAST_1", + "us-west-2", + "eu-central-1", + "ap-southeast-1", + "region_123", + "test-region", + ]; + + validRegions.forEach((region) => { + expect(schema.parse(region)).toBe(region); + }); + }); + + it("should accept exactly 50 characters", () => { + const schema = AtlasArgs.region(); + const maxLengthRegion = "a".repeat(50); + expect(schema.parse(maxLengthRegion)).toBe(maxLengthRegion); + }); + + it("should reject invalid region names", () => { + const schema = AtlasArgs.region(); + + // Empty string + expect(() => schema.parse("")).toThrow("Region is required"); + + // Too long (over 50 characters) + const longRegion = "a".repeat(51); + expect(() => schema.parse(longRegion)).toThrow("Region must be 50 characters or less"); + + // Invalid characters + expect(() => schema.parse("US EAST 1")).toThrow(ALLOWED_REGION_CHARACTERS_ERROR); + expect(() => schema.parse("US.EAST.1")).toThrow(ALLOWED_REGION_CHARACTERS_ERROR); + expect(() => schema.parse("US@EAST#1")).toThrow(ALLOWED_REGION_CHARACTERS_ERROR); + }); + }); + + describe("projectName", () => { + it("should validate valid project names", () => { + const schema = AtlasArgs.projectName(); + const validNames = [ + "my-project", + "project_1", + "Project123", + "test-project-2", + "my_project_name", + "project with spaces", + "project(with)parentheses", + "project@with@at", + "project&with&ersand", + "project+with+plus", + "project:with:colon", + "project.with.dots", + "project'with'apostrophe", + "project,with,comma", + "complex project (with) @all &symbols+here:test.name'value,", + ]; + + validNames.forEach((name) => { + expect(schema.parse(name)).toBe(name); + }); + }); + + it("should reject invalid project names", () => { + const schema = AtlasArgs.projectName(); + + // Empty string + expect(() => schema.parse("")).toThrow("Project name is required"); + + // Too long (over 64 characters) + expect(() => schema.parse("a".repeat(65))).toThrow("Project name must be 64 characters or less"); + + // Invalid characters not in the allowed set + expect(() => schema.parse("project#with#hash")).toThrow(ALLOWED_PROJECT_NAME_CHARACTERS_ERROR); + expect(() => schema.parse("project$with$dollar")).toThrow(ALLOWED_PROJECT_NAME_CHARACTERS_ERROR); + expect(() => schema.parse("project!with!exclamation")).toThrow(ALLOWED_PROJECT_NAME_CHARACTERS_ERROR); + expect(() => schema.parse("project[with]brackets")).toThrow(ALLOWED_PROJECT_NAME_CHARACTERS_ERROR); + }); + + it("should accept exactly 64 characters", () => { + const schema = AtlasArgs.projectName(); + const maxLengthName = "a".repeat(64); + expect(schema.parse(maxLengthName)).toBe(maxLengthName); + }); + }); + + describe("password", () => { + it("should validate valid passwords", () => { + const schema = AtlasArgs.password().optional(); + const validPasswords = ["password123", "password_123", "Password123", "test-password-123"]; + validPasswords.forEach((password) => { + expect(schema.parse(password)).toBe(password); + }); + expect(schema.parse(undefined)).toBeUndefined(); + }); + + it("should reject invalid passwords", () => { + const schema = AtlasArgs.password(); + expect(() => schema.parse("")).toThrow("Password is required"); + expect(() => schema.parse("a".repeat(101))).toThrow("Password must be 100 characters or less"); + }); + }); + }); + + describe("Edge Cases and Security", () => { + it("should handle empty strings appropriately", () => { + const schema = CommonArgs.string(); + expect(schema.parse("")).toBe(""); + + // But AtlasArgs validators should reject empty strings + expect(() => AtlasArgs.clusterName().parse("")).toThrow(); + expect(() => AtlasArgs.username().parse("")).toThrow(); + }); + + it("should handle very long strings", () => { + const schema = CommonArgs.string(); + const longString = "a".repeat(10000); + expect(schema.parse(longString)).toBe(longString); + + // But AtlasArgs validators should enforce length limits + expect(() => AtlasArgs.clusterName().parse("a".repeat(65))).toThrow(); + expect(() => AtlasArgs.username().parse("a".repeat(101))).toThrow(); + }); + + it("should handle null and undefined values", () => { + const schema = CommonArgs.string(); + expect(() => schema.parse(null)).toThrow(); + expect(() => schema.parse(undefined)).toThrow(); + }); + }); + + describe("Error Messages", () => { + it("should provide clear error messages for validation failures", () => { + // Test specific error messages + expect(() => AtlasArgs.clusterName().parse("")).toThrow("Cluster name is required"); + expect(() => AtlasArgs.clusterName().parse("a".repeat(65))).toThrow( + "Cluster name must be 64 characters or less" + ); + expect(() => AtlasArgs.clusterName().parse("invalid@name")).toThrow(ALLOWED_CLUSTER_NAME_CHARACTERS_ERROR); + + expect(() => AtlasArgs.username().parse("")).toThrow("Username is required"); + expect(() => AtlasArgs.username().parse("a".repeat(101))).toThrow( + "Username must be 100 characters or less" + ); + expect(() => AtlasArgs.username().parse("invalid name")).toThrow(ALLOWED_USERNAME_CHARACTERS_ERROR); + }); + }); +}); diff --git a/tests/unit/common/config.test.ts b/tests/unit/common/config.test.ts index 78257a3c0..78a0382ef 100644 --- a/tests/unit/common/config.test.ts +++ b/tests/unit/common/config.test.ts @@ -1,9 +1,31 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import type { UserConfig } from "../../../src/common/config.js"; -import { setupUserConfig, defaultUserConfig } from "../../../src/common/config.js"; +import { + setupUserConfig, + defaultUserConfig, + registerKnownSecretsInRootKeychain, + warnAboutDeprecatedOrUnknownCliArgs, +} from "../../../src/common/config.js"; +import type { CliOptions } from "@mongosh/arg-parser"; +import { Keychain } from "../../../src/common/keychain.js"; +import type { Secret } from "../../../src/common/keychain.js"; describe("config", () => { describe("env var parsing", () => { + describe("mongodb urls", () => { + it("should not try to parse a multiple-host urls", () => { + const actual = setupUserConfig({ + env: { + MDB_MCP_CONNECTION_STRING: "mongodb://user:password@host1,host2,host3/", + }, + cli: [], + defaults: defaultUserConfig, + }); + + expect(actual.connectionString).toEqual("mongodb://user:password@host1,host2,host3/"); + }); + }); + describe("string cases", () => { const testCases = [ { envVar: "MDB_MCP_API_BASE_URL", property: "apiBaseUrl", value: "http://test.com" }, @@ -19,6 +41,11 @@ describe("config", () => { { envVar: "MDB_MCP_HTTP_HOST", property: "httpHost", value: "localhost" }, { envVar: "MDB_MCP_IDLE_TIMEOUT_MS", property: "idleTimeoutMs", value: 5000 }, { envVar: "MDB_MCP_NOTIFICATION_TIMEOUT_MS", property: "notificationTimeoutMs", value: 5000 }, + { + envVar: "MDB_MCP_ATLAS_TEMPORARY_DATABASE_USER_LIFETIME_MS", + property: "atlasTemporaryDatabaseUserLifetimeMs", + value: 12345, + }, ] as const; for (const { envVar, property, value } of testCases) { @@ -59,6 +86,16 @@ describe("config", () => { }); describe("cli parsing", () => { + it("should not try to parse a multiple-host urls", () => { + const actual = setupUserConfig({ + cli: ["myself", "--", "--connectionString", "mongodb://user:password@host1,host2,host3/"], + env: {}, + defaults: defaultUserConfig, + }); + + expect(actual.connectionString).toEqual("mongodb://user:password@host1,host2,host3/"); + }); + describe("string use cases", () => { const testCases = [ { @@ -97,6 +134,10 @@ describe("config", () => { cli: ["--notificationTimeoutMs", "42"], expected: { notificationTimeoutMs: "42" }, }, + { + cli: ["--atlasTemporaryDatabaseUserLifetimeMs", "12345"], + expected: { atlasTemporaryDatabaseUserLifetimeMs: "12345" }, + }, { cli: ["--telemetry", "enabled"], expected: { telemetry: "enabled" }, @@ -605,3 +646,144 @@ describe("config", () => { }); }); }); + +describe("CLI arguments", () => { + const referDocMessage = + "Refer to https://www.mongodb.com/docs/mcp-server/get-started/ for setting up the MCP Server."; + + type TestCase = { readonly cliArg: keyof (CliOptions & UserConfig); readonly warning: string }; + const testCases = [ + { + cliArg: "connectionString", + warning: + "The --connectionString argument is deprecated. Prefer using the MDB_MCP_CONNECTION_STRING environment variable or the first positional argument for the connection string.", + }, + ] as TestCase[]; + + for (const { cliArg, warning } of testCases) { + describe(`deprecation behaviour of ${cliArg}`, () => { + let cliArgs: CliOptions & UserConfig & { _?: string[] }; + let warn: (msg: string) => void; + let exit: (status: number) => void | never; + + beforeEach(() => { + cliArgs = { [cliArg]: "RandomString" } as unknown as CliOptions & UserConfig & { _?: string[] }; + warn = vi.fn(); + exit = vi.fn(); + + warnAboutDeprecatedOrUnknownCliArgs(cliArgs as unknown as Record, { warn, exit }); + }); + + it(`warns the usage of ${cliArg} as it is deprecated`, () => { + expect(warn).toHaveBeenCalledWith(warning); + }); + + it(`shows the reference message when ${cliArg} was passed`, () => { + expect(warn).toHaveBeenCalledWith(referDocMessage); + }); + + it(`should not exit the process`, () => { + expect(exit).not.toHaveBeenCalled(); + }); + }); + } + + describe("invalid arguments", () => { + let warn: (msg: string) => void; + let exit: (status: number) => void | never; + + beforeEach(() => { + warn = vi.fn(); + exit = vi.fn(); + }); + + it("should show a warning when an argument is not known", () => { + warnAboutDeprecatedOrUnknownCliArgs( + { + wakanda: "", + }, + { warn, exit } + ); + + expect(warn).toHaveBeenCalledWith("Invalid command line argument 'wakanda'."); + expect(warn).toHaveBeenCalledWith( + "Refer to https://www.mongodb.com/docs/mcp-server/get-started/ for setting up the MCP Server." + ); + }); + + it("should exit the process on unknown cli args", () => { + warnAboutDeprecatedOrUnknownCliArgs( + { + wakanda: "", + }, + { warn, exit } + ); + + expect(exit).toHaveBeenCalledWith(1); + }); + + it("should show a suggestion when is a simple typo", () => { + warnAboutDeprecatedOrUnknownCliArgs( + { + readonli: "", + }, + { warn, exit } + ); + + expect(warn).toHaveBeenCalledWith("Invalid command line argument 'readonli'. Did you mean 'readOnly'?"); + expect(warn).toHaveBeenCalledWith( + "Refer to https://www.mongodb.com/docs/mcp-server/get-started/ for setting up the MCP Server." + ); + }); + + it("should show a suggestion when the only change is on the case", () => { + warnAboutDeprecatedOrUnknownCliArgs( + { + readonly: "", + }, + { warn, exit } + ); + + expect(warn).toHaveBeenCalledWith("Invalid command line argument 'readonly'. Did you mean 'readOnly'?"); + expect(warn).toHaveBeenCalledWith( + "Refer to https://www.mongodb.com/docs/mcp-server/get-started/ for setting up the MCP Server." + ); + }); + }); + + describe("keychain management", () => { + type TestCase = { readonly cliArg: keyof UserConfig; secretKind: Secret["kind"] }; + const testCases = [ + { cliArg: "apiClientId", secretKind: "user" }, + { cliArg: "apiClientSecret", secretKind: "password" }, + { cliArg: "awsAccessKeyId", secretKind: "password" }, + { cliArg: "awsIamSessionToken", secretKind: "password" }, + { cliArg: "awsSecretAccessKey", secretKind: "password" }, + { cliArg: "awsSessionToken", secretKind: "password" }, + { cliArg: "password", secretKind: "password" }, + { cliArg: "tlsCAFile", secretKind: "url" }, + { cliArg: "tlsCRLFile", secretKind: "url" }, + { cliArg: "tlsCertificateKeyFile", secretKind: "url" }, + { cliArg: "tlsCertificateKeyFilePassword", secretKind: "password" }, + { cliArg: "username", secretKind: "user" }, + ] as TestCase[]; + let keychain: Keychain; + + beforeEach(() => { + keychain = Keychain.root; + keychain.clearAllSecrets(); + }); + + afterEach(() => { + keychain.clearAllSecrets(); + }); + + for (const { cliArg, secretKind } of testCases) { + it(`should register ${cliArg} as a secret of kind ${secretKind} in the root keychain`, () => { + registerKnownSecretsInRootKeychain({ [cliArg]: cliArg }); + + expect(keychain.allSecrets).toEqual([{ value: cliArg, kind: secretKind }]); + }); + } + }); +}); diff --git a/tests/unit/common/exportsManager.test.ts b/tests/unit/common/exportsManager.test.ts index 8ead40fb4..bfc1eba18 100644 --- a/tests/unit/common/exportsManager.test.ts +++ b/tests/unit/common/exportsManager.test.ts @@ -5,13 +5,8 @@ import type { FindCursor } from "mongodb"; import { Long } from "mongodb"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { ExportsManagerConfig } from "../../../src/common/exportsManager.js"; -import { - ensureExtension, - isExportExpired, - ExportsManager, - validateExportName, -} from "../../../src/common/exportsManager.js"; - +import { ensureExtension, isExportExpired, ExportsManager } from "../../../src/common/exportsManager.js"; +import type { AvailableExport } from "../../../src/common/exportsManager.js"; import { config } from "../../../src/common/config.js"; import { ROOT_DIR } from "../../accuracy/sdk/constants.js"; import { timeout } from "../../integration/helpers.js"; @@ -30,14 +25,10 @@ const exportsManagerConfig: ExportsManagerConfig = { function getExportNameAndPath({ uniqueExportsId = new ObjectId().toString(), uniqueFileId = new ObjectId().toString(), - database = "foo", - collection = "bar", }: | { uniqueExportsId?: string; uniqueFileId?: string; - database?: string; - collection?: string; } | undefined = {}): { sessionExportsPath: string; @@ -46,7 +37,7 @@ function getExportNameAndPath({ exportURI: string; uniqueExportsId: string; } { - const exportName = `${database}.${collection}.${uniqueFileId}.json`; + const exportName = `${uniqueFileId}.json`; // This is the exports directory for a session. const sessionExportsPath = path.join(exportsPath, uniqueExportsId); const exportPath = path.join(sessionExportsPath, exportName); @@ -124,6 +115,15 @@ function timeoutPromise(timeoutMS: number, context: string): Promise { }); } +async function waitUntilThereIsAnExportAvailable(manager: ExportsManager): Promise { + return await vi.waitFor(() => { + const exports = manager.availableExports; + expect(exports.length).toBeGreaterThan(0); + + return exports; + }); +} + async function getExportAvailableNotifier( expectedExportURI: string, manager: ExportsManager, @@ -235,11 +235,13 @@ describe("ExportsManager unit test", () => { jsonExportFormat: "relaxed", }); await exportAvailableNotifier; - expect(await manager.readExport(exportName)).toEqual("[]"); + const { content, docsTransformed } = await manager.readExport(exportName); + expect(content).toEqual("[]"); + expect(docsTransformed).toEqual(0); }); it("should handle encoded name", async () => { - const { exportName, exportURI } = getExportNameAndPath({ database: "some database", collection: "coll" }); + const { exportName, exportURI } = getExportNameAndPath({ uniqueFileId: "1FOO 2BAR" }); const { cursor } = createDummyFindCursor([]); const exportAvailableNotifier = getExportAvailableNotifier(encodeURI(exportURI), manager); await manager.createJSONExport({ @@ -249,7 +251,9 @@ describe("ExportsManager unit test", () => { jsonExportFormat: "relaxed", }); await exportAvailableNotifier; - expect(await manager.readExport(encodeURIComponent(exportName))).toEqual("[]"); + const { content, docsTransformed } = await manager.readExport(encodeURIComponent(exportName)); + expect(content).toEqual("[]"); + expect(docsTransformed).toEqual(0); }); }); @@ -318,7 +322,8 @@ describe("ExportsManager unit test", () => { await cursorCloseNotification; // Updates available export - const availableExports = manager.availableExports; + // this is async code so we should wait and retry + const availableExports = await waitUntilThereIsAnExportAvailable(manager); expect(availableExports).toHaveLength(1); expect(availableExports).toContainEqual( expect.objectContaining({ @@ -331,7 +336,7 @@ describe("ExportsManager unit test", () => { expect(emitSpy).toHaveBeenCalledWith("export-available", exportURI); // Exports relaxed json - const jsonData = JSON.parse(await manager.readExport(exportName)) as unknown[]; + const jsonData = JSON.parse((await manager.readExport(exportName)).content) as unknown[]; expect(jsonData).toEqual([]); }); }); @@ -352,7 +357,7 @@ describe("ExportsManager unit test", () => { const expectedExportName = exportName.endsWith(".json") ? exportName : `${exportName}.json`; // Updates available export - const availableExports = manager.availableExports; + const availableExports = await waitUntilThereIsAnExportAvailable(manager); expect(availableExports).toHaveLength(1); expect(availableExports).toContainEqual( expect.objectContaining({ @@ -365,7 +370,7 @@ describe("ExportsManager unit test", () => { expect(emitSpy).toHaveBeenCalledWith("export-available", `exported-data://${expectedExportName}`); // Exports relaxed json - const jsonData = JSON.parse(await manager.readExport(expectedExportName)) as unknown[]; + const jsonData = JSON.parse((await manager.readExport(expectedExportName)).content) as unknown[]; expect(jsonData).toContainEqual(expect.objectContaining({ name: "foo", longNumber: 12 })); expect(jsonData).toContainEqual(expect.objectContaining({ name: "bar", longNumber: 123456 })); }); @@ -387,7 +392,7 @@ describe("ExportsManager unit test", () => { const expectedExportName = exportName.endsWith(".json") ? exportName : `${exportName}.json`; // Updates available export - const availableExports = manager.availableExports; + const availableExports = await waitUntilThereIsAnExportAvailable(manager); expect(availableExports).toHaveLength(1); expect(availableExports).toContainEqual( expect.objectContaining({ @@ -400,7 +405,7 @@ describe("ExportsManager unit test", () => { expect(emitSpy).toHaveBeenCalledWith("export-available", `exported-data://${expectedExportName}`); // Exports relaxed json - const jsonData = JSON.parse(await manager.readExport(expectedExportName)) as unknown[]; + const jsonData = JSON.parse((await manager.readExport(expectedExportName)).content) as unknown[]; expect(jsonData).toContainEqual( expect.objectContaining({ name: "foo", longNumber: { $numberLong: "12" } }) ); @@ -601,16 +606,6 @@ describe("#ensureExtension", () => { }); }); -describe("#validateExportName", () => { - it("should return decoded name when name is valid", () => { - expect(validateExportName(encodeURIComponent("Test Name.json"))).toEqual("Test Name.json"); - }); - it("should throw when name is invalid", () => { - expect(() => validateExportName("NoExtension")).toThrow("Provided export name has no extension"); - expect(() => validateExportName("../something.json")).toThrow("Invalid export name: path traversal hinted"); - }); -}); - describe("#isExportExpired", () => { it("should return true if export is expired", () => { const createdAt = Date.now() - 1000; diff --git a/tests/unit/common/keychain.test.ts b/tests/unit/common/keychain.test.ts new file mode 100644 index 000000000..2f0e8d246 --- /dev/null +++ b/tests/unit/common/keychain.test.ts @@ -0,0 +1,36 @@ +import { Keychain, registerGlobalSecretToRedact } from "../../../src/common/keychain.js"; +import { describe, beforeEach, afterEach, it, expect } from "vitest"; + +describe("Keychain", () => { + let keychain: Keychain; + + beforeEach(() => { + keychain = Keychain.root; + keychain.clearAllSecrets(); + }); + + afterEach(() => { + keychain.clearAllSecrets(); + }); + + it("should register a new secret", () => { + keychain.register("123456", "password"); + expect(keychain.allSecrets).toEqual([{ value: "123456", kind: "password" }]); + }); + + it("should remove cleared secrets", () => { + keychain.register("123456", "password"); + expect(keychain.allSecrets).toEqual([{ value: "123456", kind: "password" }]); + + keychain.clearAllSecrets(); + keychain.register("654321", "user"); + expect(keychain.allSecrets).toEqual([{ value: "654321", kind: "user" }]); + }); + + describe("registerGlobalSecretToRedact", () => { + it("registers the secret in the root keychain", () => { + registerGlobalSecretToRedact("123456", "password"); + expect(keychain.allSecrets).toEqual([{ value: "123456", kind: "password" }]); + }); + }); +}); diff --git a/tests/unit/common/session.test.ts b/tests/unit/common/session.test.ts index 6b2b3552b..a80520d89 100644 --- a/tests/unit/common/session.test.ts +++ b/tests/unit/common/session.test.ts @@ -4,9 +4,10 @@ import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver import { Session } from "../../../src/common/session.js"; import { config, driverOptions } from "../../../src/common/config.js"; import { CompositeLogger } from "../../../src/common/logger.js"; -import { ConnectionManager } from "../../../src/common/connectionManager.js"; +import { MCPConnectionManager } from "../../../src/common/connectionManager.js"; import { ExportsManager } from "../../../src/common/exportsManager.js"; import { DeviceId } from "../../../src/helpers/deviceId.js"; +import { Keychain } from "../../../src/common/keychain.js"; vi.mock("@mongosh/service-provider-node-driver"); @@ -27,7 +28,8 @@ describe("Session", () => { apiBaseUrl: "https://api.test.com", logger, exportsManager: ExportsManager.init(config, logger), - connectionManager: new ConnectionManager(config, driverOptions, logger, mockDeviceId), + connectionManager: new MCPConnectionManager(config, driverOptions, logger, mockDeviceId), + keychain: new Keychain(), }); MockNodeDriverServiceProvider.connect = vi.fn().mockResolvedValue({} as unknown as NodeDriverServiceProvider); diff --git a/tests/unit/elicitation.test.ts b/tests/unit/elicitation.test.ts new file mode 100644 index 000000000..eeaa81ae3 --- /dev/null +++ b/tests/unit/elicitation.test.ts @@ -0,0 +1,137 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { Elicitation } from "../../src/elicitation.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { createMockElicitInput, createMockGetClientCapabilities } from "../utils/elicitationMocks.js"; + +describe("Elicitation", () => { + let elicitation: Elicitation; + let mockGetClientCapabilities: ReturnType; + let mockElicitInput: ReturnType; + + beforeEach(() => { + mockGetClientCapabilities = createMockGetClientCapabilities(); + mockElicitInput = createMockElicitInput(); + elicitation = new Elicitation({ + server: { + getClientCapabilities: mockGetClientCapabilities, + elicitInput: mockElicitInput.mock, + } as unknown as McpServer["server"], + }); + }); + + describe("supportsElicitation", () => { + it("should return true when client supports elicitation", () => { + mockGetClientCapabilities.mockReturnValue({ elicitation: {} }); + + const result = elicitation.supportsElicitation(); + + expect(result).toBe(true); + expect(mockGetClientCapabilities).toHaveBeenCalledTimes(1); + }); + + it("should return false when client does not support elicitation", () => { + mockGetClientCapabilities.mockReturnValue({}); + + const result = elicitation.supportsElicitation(); + + expect(result).toBe(false); + expect(mockGetClientCapabilities).toHaveBeenCalledTimes(1); + }); + + it("should return false when client capabilities are undefined", () => { + mockGetClientCapabilities.mockReturnValue(undefined); + + const result = elicitation.supportsElicitation(); + + expect(result).toBe(false); + expect(mockGetClientCapabilities).toHaveBeenCalledTimes(1); + }); + + it("should return false when elicitation capability is explicitly undefined", () => { + mockGetClientCapabilities.mockReturnValue(undefined); + + const result = elicitation.supportsElicitation(); + + expect(result).toBe(false); + expect(mockGetClientCapabilities).toHaveBeenCalledTimes(1); + }); + }); + + describe("requestConfirmation", () => { + const testMessage = "Are you sure you want to proceed?"; + + it("should return true when client does not support elicitation", async () => { + mockGetClientCapabilities.mockReturnValue({}); + + const result = await elicitation.requestConfirmation(testMessage); + + expect(result).toBe(true); + expect(mockGetClientCapabilities).toHaveBeenCalledTimes(1); + expect(mockElicitInput.mock).not.toHaveBeenCalled(); + }); + + it("should return true when user confirms with 'Yes' and action is 'accept'", async () => { + mockGetClientCapabilities.mockReturnValue({ elicitation: {} }); + mockElicitInput.confirmYes(); + + const result = await elicitation.requestConfirmation(testMessage); + + expect(result).toBe(true); + expect(mockGetClientCapabilities).toHaveBeenCalledTimes(1); + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + expect(mockElicitInput.mock).toHaveBeenCalledWith({ + message: testMessage, + requestedSchema: Elicitation.CONFIRMATION_SCHEMA, + }); + }); + + it("should return false when user selects 'No' with action 'accept'", async () => { + mockGetClientCapabilities.mockReturnValue({ elicitation: {} }); + mockElicitInput.confirmNo(); + + const result = await elicitation.requestConfirmation(testMessage); + + expect(result).toBe(false); + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + }); + + it("should return false when content is undefined", async () => { + mockGetClientCapabilities.mockReturnValue({ elicitation: {} }); + mockElicitInput.acceptWith(undefined); + + const result = await elicitation.requestConfirmation(testMessage); + + expect(result).toBe(false); + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + }); + + it("should return false when confirmation field is missing", async () => { + mockGetClientCapabilities.mockReturnValue({ elicitation: {} }); + mockElicitInput.acceptWith({}); + + const result = await elicitation.requestConfirmation(testMessage); + + expect(result).toBe(false); + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + }); + + it("should return false when user cancels", async () => { + mockGetClientCapabilities.mockReturnValue({ elicitation: {} }); + mockElicitInput.cancel(); + + const result = await elicitation.requestConfirmation(testMessage); + + expect(result).toBe(false); + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + }); + + it("should handle elicitInput erroring", async () => { + mockGetClientCapabilities.mockReturnValue({ elicitation: {} }); + const error = new Error("Elicitation failed"); + mockElicitInput.rejectWith(error); + + await expect(elicitation.requestConfirmation(testMessage)).rejects.toThrow("Elicitation failed"); + expect(mockElicitInput.mock).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/tests/unit/logger.test.ts b/tests/unit/logger.test.ts index 6bddc782d..1868924e0 100644 --- a/tests/unit/logger.test.ts +++ b/tests/unit/logger.test.ts @@ -7,10 +7,13 @@ import * as path from "path"; import * as fs from "fs/promises"; import { once } from "events"; import type { Server } from "../../src/server.js"; +import { LoggingMessageNotificationSchema } from "@modelcontextprotocol/sdk/types.js"; +import { Keychain } from "../../src/common/keychain.js"; describe("Logger", () => { let consoleErrorSpy: MockInstance; let consoleLogger: ConsoleLogger; + let keychain: Keychain; let mcpLoggerSpy: MockInstance; let mcpLogger: McpLogger; @@ -19,25 +22,30 @@ describe("Logger", () => { beforeEach(() => { // Mock console.error before creating the ConsoleLogger consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + keychain = Keychain.root; - consoleLogger = new ConsoleLogger(); + consoleLogger = new ConsoleLogger(keychain); mcpLoggerSpy = vi.fn(); minimumMcpLogLevel = "debug"; - mcpLogger = new McpLogger({ - mcpServer: { - server: { - sendLoggingMessage: mcpLoggerSpy, + mcpLogger = new McpLogger( + { + mcpServer: { + server: { + sendLoggingMessage: mcpLoggerSpy, + }, + isConnected: () => true, }, - isConnected: () => true, - }, - get mcpLogLevel() { - return minimumMcpLogLevel; - }, - } as unknown as Server); + get mcpLogLevel() { + return minimumMcpLogLevel; + }, + } as unknown as Server, + keychain + ); }); afterEach(() => { + keychain.clearAllSecrets(); vi.restoreAllMocks(); }); @@ -80,6 +88,16 @@ describe("Logger", () => { expectLogMessageRedaction(getLastMcpLogMessage(), false); }); + it("redacts sensitive information from the keychain", () => { + keychain.register("123456", "password"); + consoleLogger.info({ id: LogId.serverInitialized, context: "test", message: "Your password is 123456." }); + + expect(consoleErrorSpy).toHaveBeenCalledOnce(); + + expect(getLastConsoleMessage()).to.contain("Your password is "); + expect(getLastConsoleMessage()).to.not.contain("123456"); + }); + it("allows disabling redaction for all loggers", () => { const payload = { ...mockSensitivePayload, @@ -195,9 +213,13 @@ describe("Logger", () => { }; it("buffers messages during initialization", async () => { - const diskLogger = new DiskLogger(logPath, (err) => { - expect.fail(`Disk logger should not fail to initialize: ${err}`); - }); + const diskLogger = new DiskLogger( + logPath, + (err) => { + expect.fail(`Disk logger should not fail to initialize: ${err}`); + }, + keychain + ); diskLogger.info({ id: LogId.serverInitialized, context: "test", message: "Test message" }); await assertNoLogs(); @@ -211,9 +233,13 @@ describe("Logger", () => { }); it("includes attributes in the logs", async () => { - const diskLogger = new DiskLogger(logPath, (err) => { - expect.fail(`Disk logger should not fail to initialize: ${err}`); - }); + const diskLogger = new DiskLogger( + logPath, + (err) => { + expect.fail(`Disk logger should not fail to initialize: ${err}`); + }, + keychain + ); diskLogger.info({ id: LogId.serverInitialized, @@ -332,5 +358,9 @@ describe("Logger", () => { expect(mcpLoggerSpy).toHaveBeenCalledTimes(2); expect(getLastMcpLogMessage()).toContain("Alert message"); }); + + it("MCPLogger.LOG_LEVELS contains all possible levels", () => { + expect(McpLogger.LOG_LEVELS).toEqual(LoggingMessageNotificationSchema.shape.params.shape.level.options); + }); }); }); diff --git a/tests/unit/resources/common/debug.test.ts b/tests/unit/resources/common/debug.test.ts index 52f1dd826..e408dd4ba 100644 --- a/tests/unit/resources/common/debug.test.ts +++ b/tests/unit/resources/common/debug.test.ts @@ -4,9 +4,10 @@ import { Session } from "../../../../src/common/session.js"; import { Telemetry } from "../../../../src/telemetry/telemetry.js"; import { config, driverOptions } from "../../../../src/common/config.js"; import { CompositeLogger } from "../../../../src/common/logger.js"; -import { ConnectionManager } from "../../../../src/common/connectionManager.js"; +import { MCPConnectionManager } from "../../../../src/common/connectionManager.js"; import { ExportsManager } from "../../../../src/common/exportsManager.js"; import { DeviceId } from "../../../../src/helpers/deviceId.js"; +import { Keychain } from "../../../../src/common/keychain.js"; describe("debug resource", () => { const logger = new CompositeLogger(); @@ -15,7 +16,8 @@ describe("debug resource", () => { apiBaseUrl: "", logger, exportsManager: ExportsManager.init(config, logger), - connectionManager: new ConnectionManager(config, driverOptions, logger, deviceId), + connectionManager: new MCPConnectionManager(config, driverOptions, logger, deviceId), + keychain: new Keychain(), }); const telemetry = Telemetry.create(session, { ...config, telemetry: "disabled" }, deviceId); diff --git a/tests/unit/telemetry.test.ts b/tests/unit/telemetry.test.ts index 8ce1de2d0..9db8ea953 100644 --- a/tests/unit/telemetry.test.ts +++ b/tests/unit/telemetry.test.ts @@ -1,13 +1,14 @@ import { ApiClient } from "../../src/common/atlas/apiClient.js"; import type { Session } from "../../src/common/session.js"; import { Telemetry } from "../../src/telemetry/telemetry.js"; -import type { BaseEvent, TelemetryResult } from "../../src/telemetry/types.js"; +import type { BaseEvent, CommonProperties, TelemetryEvent, TelemetryResult } from "../../src/telemetry/types.js"; import { EventCache } from "../../src/telemetry/eventCache.js"; import { config } from "../../src/common/config.js"; import { afterEach, beforeEach, describe, it, vi, expect } from "vitest"; import { NullLogger } from "../../src/common/logger.js"; import type { MockedFunction } from "vitest"; import type { DeviceId } from "../../src/helpers/deviceId.js"; +import { expectDefined } from "../integration/helpers.js"; // Mock the ApiClient to avoid real API calls vi.mock("../../src/common/atlas/apiClient.js"); @@ -23,12 +24,13 @@ describe("Telemetry", () => { hasCredentials: MockedFunction<() => boolean>; }; let mockEventCache: { - getEvents: MockedFunction<() => BaseEvent[]>; - clearEvents: MockedFunction<() => Promise>; + getEvents: MockedFunction<() => { id: number; event: BaseEvent }[]>; + removeEvents: MockedFunction<(ids: number[]) => Promise>; appendEvents: MockedFunction<(events: BaseEvent[]) => Promise>; }; let session: Session; let telemetry: Telemetry; + let mockDeviceId: DeviceId; // Helper function to create properly typed test events function createTestEvent(options?: { @@ -59,26 +61,36 @@ describe("Telemetry", () => { }; } + function emitEventsForTest(events: BaseEvent[]): Promise { + return new Promise((resolve) => { + telemetry.events.once("events-emitted", resolve); + telemetry.events.once("events-send-failed", resolve); + telemetry.events.once("events-skipped", resolve); + + telemetry.emitEvents(events); + }); + } + // Helper function to verify mock calls to reduce duplication function verifyMockCalls({ sendEventsCalls = 0, - clearEventsCalls = 0, + removeEventsCalls = 0, appendEventsCalls = 0, sendEventsCalledWith = undefined, appendEventsCalledWith = undefined, }: { sendEventsCalls?: number; - clearEventsCalls?: number; + removeEventsCalls?: number; appendEventsCalls?: number; sendEventsCalledWith?: BaseEvent[] | undefined; appendEventsCalledWith?: BaseEvent[] | undefined; } = {}): void { const { calls: sendEvents } = mockApiClient.sendEvents.mock; - const { calls: clearEvents } = mockEventCache.clearEvents.mock; + const { calls: removeEvents } = mockEventCache.removeEvents.mock; const { calls: appendEvents } = mockEventCache.appendEvents.mock; expect(sendEvents.length).toBe(sendEventsCalls); - expect(clearEvents.length).toBe(clearEventsCalls); + expect(removeEvents.length).toBe(removeEventsCalls); expect(appendEvents.length).toBe(appendEventsCalls); if (sendEventsCalledWith) { @@ -111,11 +123,11 @@ describe("Telemetry", () => { // Setup mocked EventCache mockEventCache = new MockEventCache() as unknown as typeof mockEventCache; mockEventCache.getEvents = vi.fn().mockReturnValue([]); - mockEventCache.clearEvents = vi.fn().mockResolvedValue(undefined); + mockEventCache.removeEvents = vi.fn().mockResolvedValue(undefined); mockEventCache.appendEvents = vi.fn().mockResolvedValue(undefined); MockEventCache.getInstance = vi.fn().mockReturnValue(mockEventCache as unknown as EventCache); - const mockDeviceId = { + mockDeviceId = { get: vi.fn().mockResolvedValue("test-device-id"), } as unknown as DeviceId; @@ -137,183 +149,200 @@ describe("Telemetry", () => { config.telemetry = "enabled"; }); - describe("sending events", () => { - describe("when telemetry is enabled", () => { - it("should send events successfully", async () => { - const testEvent = createTestEvent(); + describe("when telemetry is enabled", () => { + it("should send events successfully", async () => { + const testEvent = createTestEvent(); - await telemetry.setupPromise; + await telemetry.setupPromise; - await telemetry.emitEvents([testEvent]); + await emitEventsForTest([testEvent]); - verifyMockCalls({ - sendEventsCalls: 1, - clearEventsCalls: 1, - sendEventsCalledWith: [testEvent], - }); + verifyMockCalls({ + sendEventsCalls: 1, + removeEventsCalls: 1, + sendEventsCalledWith: [testEvent], }); + }); - it("should cache events when sending fails", async () => { - mockApiClient.sendEvents.mockRejectedValueOnce(new Error("API error")); + it("should cache events when sending fails", async () => { + mockApiClient.sendEvents.mockRejectedValueOnce(new Error("API error")); - const testEvent = createTestEvent(); + const testEvent = createTestEvent(); - await telemetry.setupPromise; + await telemetry.setupPromise; - await telemetry.emitEvents([testEvent]); + await emitEventsForTest([testEvent]); - verifyMockCalls({ - sendEventsCalls: 1, - appendEventsCalls: 1, - appendEventsCalledWith: [testEvent], - }); + verifyMockCalls({ + sendEventsCalls: 1, + appendEventsCalls: 1, + appendEventsCalledWith: [testEvent], }); + }); - it("should include cached events when sending", async () => { - const cachedEvent = createTestEvent({ - command: "cached-command", - component: "cached-component", - }); + it("should include cached events when sending", async () => { + const cachedEvent = createTestEvent({ + command: "cached-command", + component: "cached-component", + }); - const newEvent = createTestEvent({ - command: "new-command", - component: "new-component", - }); + const newEvent = createTestEvent({ + command: "new-command", + component: "new-component", + }); - // Set up mock to return cached events - mockEventCache.getEvents.mockReturnValueOnce([cachedEvent]); + // Set up mock to return cached events + mockEventCache.getEvents.mockReturnValueOnce([{ id: 0, event: cachedEvent }]); - await telemetry.setupPromise; + await telemetry.setupPromise; - await telemetry.emitEvents([newEvent]); + await emitEventsForTest([newEvent]); - verifyMockCalls({ - sendEventsCalls: 1, - clearEventsCalls: 1, - sendEventsCalledWith: [cachedEvent, newEvent], - }); + verifyMockCalls({ + sendEventsCalls: 1, + removeEventsCalls: 1, + sendEventsCalledWith: [cachedEvent, newEvent], }); + }); - it("should correctly add common properties to events", async () => { - await telemetry.setupPromise; + it("should correctly add common properties to events", async () => { + await telemetry.setupPromise; + + const commonProps = telemetry.getCommonProperties(); - const commonProps = telemetry.getCommonProperties(); + // Use explicit type assertion + const expectedProps: Record = { + mcp_client_version: "1.0.0", + mcp_client_name: "test-agent", + session_id: "test-session-id", + config_atlas_auth: "true", + config_connection_string: expect.any(String) as unknown as string, + device_id: "test-device-id", + }; - // Use explicit type assertion - const expectedProps: Record = { - mcp_client_version: "1.0.0", - mcp_client_name: "test-agent", - session_id: "test-session-id", - config_atlas_auth: "true", - config_connection_string: expect.any(String) as unknown as string, - device_id: "test-device-id", - }; + expect(commonProps).toMatchObject(expectedProps); + }); - expect(commonProps).toMatchObject(expectedProps); + it("should add hostingMode to events if set", async () => { + telemetry = Telemetry.create(session, config, mockDeviceId, { + eventCache: mockEventCache as unknown as EventCache, + commonProperties: { hosting_mode: "vscode-extension" }, }); + await telemetry.setupPromise; + + const commonProps = telemetry.getCommonProperties(); + expect(commonProps.hosting_mode).toBe("vscode-extension"); - describe("device ID resolution", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); + await emitEventsForTest([createTestEvent()]); - afterEach(() => { - vi.clearAllMocks(); - }); + const calls = mockApiClient.sendEvents.mock.calls; + expect(calls).toHaveLength(1); + const event = calls[0]?.[0][0]; + expectDefined(event); + expect((event as TelemetryEvent).properties.hosting_mode).toBe("vscode-extension"); + }); + + describe("device ID resolution", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); - it("should successfully resolve the device ID", async () => { - const mockDeviceId = { - get: vi.fn().mockResolvedValue("test-device-id"), - } as unknown as DeviceId; + it("should successfully resolve the device ID", async () => { + const mockDeviceId = { + get: vi.fn().mockResolvedValue("test-device-id"), + } as unknown as DeviceId; - telemetry = Telemetry.create(session, config, mockDeviceId); + telemetry = Telemetry.create(session, config, mockDeviceId); - expect(telemetry["isBufferingEvents"]).toBe(true); - expect(telemetry.getCommonProperties().device_id).toBe(undefined); + expect(telemetry["isBufferingEvents"]).toBe(true); + expect(telemetry.getCommonProperties().device_id).toBe(undefined); - await telemetry.setupPromise; + await telemetry.setupPromise; - expect(telemetry["isBufferingEvents"]).toBe(false); - expect(telemetry.getCommonProperties().device_id).toBe("test-device-id"); - }); + expect(telemetry["isBufferingEvents"]).toBe(false); + expect(telemetry.getCommonProperties().device_id).toBe("test-device-id"); + }); - it("should handle device ID resolution failure gracefully", async () => { - const mockDeviceId = { - get: vi.fn().mockResolvedValue("unknown"), - } as unknown as DeviceId; + it("should handle device ID resolution failure gracefully", async () => { + const mockDeviceId = { + get: vi.fn().mockResolvedValue("unknown"), + } as unknown as DeviceId; - telemetry = Telemetry.create(session, config, mockDeviceId); + telemetry = Telemetry.create(session, config, mockDeviceId); - expect(telemetry["isBufferingEvents"]).toBe(true); - expect(telemetry.getCommonProperties().device_id).toBe(undefined); + expect(telemetry["isBufferingEvents"]).toBe(true); + expect(telemetry.getCommonProperties().device_id).toBe(undefined); - await telemetry.setupPromise; + await telemetry.setupPromise; - expect(telemetry["isBufferingEvents"]).toBe(false); - // Should use "unknown" as fallback when device ID resolution fails - expect(telemetry.getCommonProperties().device_id).toBe("unknown"); - }); + expect(telemetry["isBufferingEvents"]).toBe(false); + // Should use "unknown" as fallback when device ID resolution fails + expect(telemetry.getCommonProperties().device_id).toBe("unknown"); + }); - it("should handle device ID timeout gracefully", async () => { - const mockDeviceId = { - get: vi.fn().mockResolvedValue("unknown"), - } as unknown as DeviceId; + it("should handle device ID timeout gracefully", async () => { + const mockDeviceId = { + get: vi.fn().mockResolvedValue("unknown"), + } as unknown as DeviceId; - telemetry = Telemetry.create(session, config, mockDeviceId); + telemetry = Telemetry.create(session, config, mockDeviceId); - expect(telemetry["isBufferingEvents"]).toBe(true); - expect(telemetry.getCommonProperties().device_id).toBe(undefined); + expect(telemetry["isBufferingEvents"]).toBe(true); + expect(telemetry.getCommonProperties().device_id).toBe(undefined); - await telemetry.setupPromise; + await telemetry.setupPromise; - expect(telemetry["isBufferingEvents"]).toBe(false); - // Should use "unknown" as fallback when device ID times out - expect(telemetry.getCommonProperties().device_id).toBe("unknown"); - }); + expect(telemetry["isBufferingEvents"]).toBe(false); + // Should use "unknown" as fallback when device ID times out + expect(telemetry.getCommonProperties().device_id).toBe("unknown"); }); }); + }); - describe("when telemetry is disabled", () => { - beforeEach(() => { - config.telemetry = "disabled"; - }); + describe("when telemetry is disabled", () => { + beforeEach(() => { + config.telemetry = "disabled"; + }); - afterEach(() => { - config.telemetry = "enabled"; - }); + afterEach(() => { + config.telemetry = "enabled"; + }); - it("should not send events", async () => { - const testEvent = createTestEvent(); + it("should not send events", async () => { + const testEvent = createTestEvent(); - await telemetry.emitEvents([testEvent]); + await emitEventsForTest([testEvent]); - verifyMockCalls(); - }); + verifyMockCalls(); }); + }); - describe("when DO_NOT_TRACK environment variable is set", () => { - let originalEnv: string | undefined; + describe("when DO_NOT_TRACK environment variable is set", () => { + let originalEnv: string | undefined; - beforeEach(() => { - originalEnv = process.env.DO_NOT_TRACK; - process.env.DO_NOT_TRACK = "1"; - }); + beforeEach(() => { + originalEnv = process.env.DO_NOT_TRACK; + process.env.DO_NOT_TRACK = "1"; + }); - afterEach(() => { - if (originalEnv) { - process.env.DO_NOT_TRACK = originalEnv; - } else { - delete process.env.DO_NOT_TRACK; - } - }); + afterEach(() => { + if (originalEnv) { + process.env.DO_NOT_TRACK = originalEnv; + } else { + delete process.env.DO_NOT_TRACK; + } + }); - it("should not send events", async () => { - const testEvent = createTestEvent(); + it("should not send events", async () => { + const testEvent = createTestEvent(); - await telemetry.emitEvents([testEvent]); + await emitEventsForTest([testEvent]); - verifyMockCalls(); - }); + verifyMockCalls(); }); }); }); diff --git a/tests/unit/toolBase.test.ts b/tests/unit/toolBase.test.ts new file mode 100644 index 000000000..0e7d958c8 --- /dev/null +++ b/tests/unit/toolBase.test.ts @@ -0,0 +1,129 @@ +import { describe, it, expect, vi, beforeEach, type MockedFunction } from "vitest"; +import { z } from "zod"; +import { ToolBase, type OperationType, type ToolCategory, type ToolConstructorParams } from "../../src/tools/tool.js"; +import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { Session } from "../../src/common/session.js"; +import type { UserConfig } from "../../src/common/config.js"; +import type { Telemetry } from "../../src/telemetry/telemetry.js"; +import type { Elicitation } from "../../src/elicitation.js"; +import type { CompositeLogger } from "../../src/common/logger.js"; +import type { TelemetryToolMetadata, ToolCallbackArgs } from "../../src/tools/tool.js"; + +describe("ToolBase", () => { + let mockSession: Session; + let mockLogger: CompositeLogger; + let mockConfig: UserConfig; + let mockTelemetry: Telemetry; + let mockElicitation: Elicitation; + let mockRequestConfirmation: MockedFunction<(message: string) => Promise>; + let testTool: TestTool; + + beforeEach(() => { + mockLogger = { + info: vi.fn(), + debug: vi.fn(), + warning: vi.fn(), + error: vi.fn(), + } as unknown as CompositeLogger; + + mockSession = { + logger: mockLogger, + } as Session; + + mockConfig = { + confirmationRequiredTools: [], + } as unknown as UserConfig; + + mockTelemetry = {} as Telemetry; + + mockRequestConfirmation = vi.fn(); + mockElicitation = { + requestConfirmation: mockRequestConfirmation, + } as unknown as Elicitation; + + const constructorParams: ToolConstructorParams = { + session: mockSession, + config: mockConfig, + telemetry: mockTelemetry, + elicitation: mockElicitation, + }; + + testTool = new TestTool(constructorParams); + }); + + describe("verifyConfirmed", () => { + it("should return true when tool is not in confirmationRequiredTools list", async () => { + mockConfig.confirmationRequiredTools = ["other-tool", "another-tool"]; + + const args = [ + { param1: "test" }, + {} as ToolCallbackArgs<(typeof testTool)["argsShape"]>[1], + ] as ToolCallbackArgs<(typeof testTool)["argsShape"]>; + const result = await testTool.verifyConfirmed(args); + + expect(result).toBe(true); + expect(mockRequestConfirmation).not.toHaveBeenCalled(); + }); + + it("should return true when confirmationRequiredTools list is empty", async () => { + mockConfig.confirmationRequiredTools = []; + + const args = [{ param1: "test" }, {} as ToolCallbackArgs<(typeof testTool)["argsShape"]>[1]]; + const result = await testTool.verifyConfirmed(args as ToolCallbackArgs<(typeof testTool)["argsShape"]>); + + expect(result).toBe(true); + expect(mockRequestConfirmation).not.toHaveBeenCalled(); + }); + + it("should call requestConfirmation when tool is in confirmationRequiredTools list", async () => { + mockConfig.confirmationRequiredTools = ["test-tool"]; + mockRequestConfirmation.mockResolvedValue(true); + + const args = [{ param1: "test", param2: 42 }, {} as ToolCallbackArgs<(typeof testTool)["argsShape"]>[1]]; + const result = await testTool.verifyConfirmed(args as ToolCallbackArgs<(typeof testTool)["argsShape"]>); + + expect(result).toBe(true); + expect(mockRequestConfirmation).toHaveBeenCalledTimes(1); + expect(mockRequestConfirmation).toHaveBeenCalledWith( + "You are about to execute the `test-tool` tool which requires additional confirmation. Would you like to proceed?" + ); + }); + + it("should return false when user declines confirmation", async () => { + mockConfig.confirmationRequiredTools = ["test-tool"]; + mockRequestConfirmation.mockResolvedValue(false); + + const args = [{ param1: "test" }, {} as ToolCallbackArgs<(typeof testTool)["argsShape"]>[1]]; + const result = await testTool.verifyConfirmed(args as ToolCallbackArgs<(typeof testTool)["argsShape"]>); + + expect(result).toBe(false); + expect(mockRequestConfirmation).toHaveBeenCalledTimes(1); + }); + }); +}); + +class TestTool extends ToolBase { + public name = "test-tool"; + public category: ToolCategory = "mongodb"; + public operationType: OperationType = "delete"; + protected description = "A test tool for verification tests"; + protected argsShape = { + param1: z.string().describe("Test parameter 1"), + param2: z.number().optional().describe("Test parameter 2"), + }; + + protected async execute(): Promise { + return Promise.resolve({ + content: [ + { + type: "text", + text: "Test tool executed successfully", + }, + ], + }); + } + + protected resolveTelemetryMetadata(): TelemetryToolMetadata { + return {}; + } +} diff --git a/tests/utils/elicitationMocks.ts b/tests/utils/elicitationMocks.ts new file mode 100644 index 000000000..da128fd90 --- /dev/null +++ b/tests/utils/elicitationMocks.ts @@ -0,0 +1,67 @@ +import type { MockedFunction } from "vitest"; +import { vi } from "vitest"; + +/** + * Mock types based on the MCP SDK types, but simplified for testing + */ +export type MockClientCapabilities = { + [x: string]: unknown; + elicitation?: { [x: string]: unknown }; +}; + +export type MockElicitInput = { + message: string; + requestedSchema: unknown; +}; + +export type MockElicitResult = { + action: string; + content?: { + confirmation?: string; + }; +}; + +/** + * Creates mock functions for elicitation testing + */ +export function createMockElicitInput(): { + mock: MockedFunction<() => Promise>; + confirmYes: () => void; + confirmNo: () => void; + acceptWith: (content: { confirmation?: string } | undefined) => void; + cancel: () => void; + rejectWith: (error: Error) => void; + clear: () => void; +} { + const mockFn = vi.fn(); + + return { + mock: mockFn, + confirmYes: () => + mockFn.mockResolvedValue({ + action: "accept", + content: { confirmation: "Yes" }, + }), + confirmNo: () => + mockFn.mockResolvedValue({ + action: "accept", + content: { confirmation: "No" }, + }), + acceptWith: (content: { confirmation?: string } | undefined) => + mockFn.mockResolvedValue({ + action: "accept", + content, + }), + cancel: () => + mockFn.mockResolvedValue({ + action: "cancel", + content: undefined, + }), + rejectWith: (error: Error) => mockFn.mockRejectedValue(error), + clear: () => mockFn.mockClear(), + }; +} + +export function createMockGetClientCapabilities(): MockedFunction<() => MockClientCapabilities | undefined> { + return vi.fn(); +} diff --git a/tsconfig.build.json b/tsconfig.build.json index b21c6771e..06089861b 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -17,7 +17,13 @@ "typeRoots": ["./node_modules/@types", "./src/types"], "noImplicitReturns": true, "declaration": true, - "declarationMap": true + "declarationMap": true, + "paths": { + "mongodb-connection-string-url": [ + "./node_modules/mongodb-connection-string-url/lib/index.d.ts" + ], + "ts-levenshtein": ["./node_modules/ts-levenshtein/dist/index.d.mts"] + } }, "include": ["src/**/*.ts"] }