diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..05384e6f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +dist +node_modules +.vscode +.github +.git +# Environment variables +.env + +tests +coverage +scripts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..d68a96d3 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @mongodb-js/mcp-server-developers diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..87e2e0d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,48 @@ +--- +name: "Bug Report" +description: "Report a bug to help us improve." +title: "[Bug]: " +type: "Bug" +body: + - type: markdown + attributes: + value: "Please fill out the following details to help us address the issue." + - type: textarea + id: version + attributes: + label: "Version" + description: "Please provide the version of the MCP Server where the bug occurred. (e.g., 0.1.0, main branch sha, etc.)" + placeholder: "e.g., 0.1.0" + validations: + required: true + - type: checkboxes + id: app + attributes: + label: "App" + description: "Select the app where the bug occurred." + options: + - label: Cursor + - label: Windsurf + - label: VSCode + - label: VSCode Insiders + - label: Claude Desktop + - label: Other + - type: checkboxes + id: affected_models + attributes: + label: "Affected Models (if applicable)" + description: "Select the models affected by the bug." + options: + - label: "Claude 3.5 Sonnet" + - label: "Claude 3.7 Sonnet" + - label: "GPT-4a" + - label: "o4-mini" + - label: "Other" + - type: textarea + id: description + attributes: + label: "Bug Description" + description: "Describe the bug in detail. Include steps to reproduce, expected behavior, and actual behavior." + placeholder: "e.g., When I prompt connect to mongodb, it crashes with error XYZ." + validations: + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..782a0ad7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml new file mode 100644 index 00000000..f6785dd3 --- /dev/null +++ b/.github/workflows/check-pr-title.yml @@ -0,0 +1,29 @@ +name: "Check PR Title" +on: + pull_request: + types: + [ + opened, + synchronize, + reopened, + ready_for_review, + labeled, + unlabeled, + converted_to_draft, + edited, + ] + +permissions: + pull-requests: read # to read PR title and labels + +jobs: + check-pr-title: + name: Check PR Title + runs-on: ubuntu-latest + steps: + - name: Enforce conventional commit style + uses: realm/ci-actions/title-checker@d6cc8f067474759d38e6d24e272027b4c88bc0a9 + with: + regex: '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|ops){1}(\([\w\-\.]+\))?(!)?: .*' + error-hint: 'Invalid PR title. Make sure it follows the conventional commit specification (i.e. "(): ") or add the no-title-validation label' + ignore-labels: "no-title-validation" diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 00000000..71a5b657 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,58 @@ +--- +name: Checks +on: + push: + branches: + - main + pull_request: + pull_request_target: + branches: + - main + +permissions: {} + +jobs: + check-style: + runs-on: ubuntu-latest + steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: package.json + cache: "npm" + - name: Install dependencies + run: npm ci + - name: Run style check + run: npm run check + + check-generate: + runs-on: ubuntu-latest + steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: package.json + cache: "npm" + - name: Install dependencies + run: npm ci + - run: npm run generate + + check-dep: + name: Check dependencies + runs-on: ubuntu-latest + steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: package.json + cache: "npm" + - name: Install dependencies, build and remove dev dependencies + run: | + npm ci + rm -rf node_modules + npm pkg set scripts.prepare="exit 0" + npm install --omit=dev + - run: npx -y @modelcontextprotocol/inspector --cli --method tools/list -- node dist/index.js --connectionString "mongodb://localhost" diff --git a/.github/workflows/code_health.yaml b/.github/workflows/code_health.yaml index 25d81b74..2f8ed17a 100644 --- a/.github/workflows/code_health.yaml +++ b/.github/workflows/code_health.yaml @@ -5,35 +5,13 @@ on: branches: - main pull_request: -jobs: - check-style: - runs-on: ubuntu-latest - steps: - - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version-file: package.json - cache: "npm" - - name: Install dependencies - run: npm ci - - name: Run style check - run: npm run check - check-generate: - runs-on: ubuntu-latest - steps: - - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version-file: package.json - cache: "npm" - - name: Install dependencies - run: npm ci - - run: npm run generate +permissions: {} +jobs: run-tests: + name: Run MongoDB tests + if: github.event.pull_request.user.login != 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] @@ -59,6 +37,8 @@ jobs: path: coverage/lcov.info run-atlas-tests: + name: Run Atlas tests + if: github.event.pull_request.user.login != 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 @@ -74,17 +54,19 @@ 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 -- --testPathIgnorePatterns "tests/integration/tools/mongodb" --testPathIgnorePatterns "tests/integration/[^/]+\.ts" + run: npm test -- --testPathIgnorePatterns "tests/unit" --testPathIgnorePatterns "tests/integration/tools/mongodb" --testPathIgnorePatterns "tests/integration/[^/]+\.ts" - name: Upload test results uses: actions/upload-artifact@v4 if: always() with: name: atlas-test-results path: coverage/lcov.info + coverage: + name: Report Coverage + if: always() && github.event.pull_request.user.login != 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest needs: [run-tests, run-atlas-tests] - if: always() steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -110,5 +92,3 @@ jobs: uses: coverallsapp/github-action@v2.3.6 with: file: coverage/lcov.info - git-branch: ${{ github.head_ref || github.ref_name }} - git-commit: ${{ github.event.pull_request.head.sha || github.sha }} diff --git a/.github/workflows/code_health_fork.yaml b/.github/workflows/code_health_fork.yaml new file mode 100644 index 00000000..3704ddbc --- /dev/null +++ b/.github/workflows/code_health_fork.yaml @@ -0,0 +1,50 @@ +--- +name: Code Health (fork) +on: + pull_request_target: + branches: + - main + +permissions: {} + +jobs: + run-tests: + name: Run MongoDB tests + if: github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.head.repo.full_name != github.repository + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + if: matrix.os != 'windows-latest' + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: package.json + cache: "npm" + - name: Install dependencies + run: npm ci + - name: Run tests + run: npm test + - name: Upload test results + if: always() && matrix.os == 'ubuntu-latest' + uses: actions/upload-artifact@v4 + with: + name: test-results + path: coverage/lcov.info + + merge-dependabot-pr: + name: Merge Dependabot PR + if: github.event.pull_request.user.login == 'dependabot[bot]' + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + steps: + - name: Enable auto-merge for Dependabot PRs + run: gh pr merge --auto --squash "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..34549e44 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,34 @@ +name: "CodeQL Advanced" + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + schedule: + - cron: "35 4 * * 4" + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + security-events: write + + strategy: + fail-fast: false + matrix: + language: + - actions + - javascript-typescript + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 00000000..2fd17cf7 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,57 @@ +name: Daily Release Docker Image +on: + schedule: + - cron: "0 1 * * *" # Every day at 1:00 AM + workflow_dispatch: # Run the action manually +permissions: + contents: read + issues: write +jobs: + push: + runs-on: ubuntu-latest + steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + with: + config: ${{ vars.PERMISSIONS_CONFIG }} + - name: Check out code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@18ce135bb5112fa8ce4ed6c17ab05699d7f3a5e0 + - name: Login to Docker Hub + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 + with: + username: "${{ secrets.DOCKERHUB_USERNAME }}" + password: "${{ secrets.DOCKERHUB_PASSWORD }}" + - name: Set date and version + id: set-properties + run: | + DATE=$(date +'%Y-%m-%d') + VERSION=$(npm pkg get version | tr -d '"') + echo "DATE=${DATE}" >> "$GITHUB_OUTPUT" + echo "VERSION=${VERSION}" >> "$GITHUB_OUTPUT" + - name: Build and push image to dockerhub registry + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 + with: + context: . + platforms: linux/amd64,linux/arm64 + tags: ${{ vars.DOCKERHUB_IMAGE_REPOSITORY }}:latest, ${{ vars.DOCKERHUB_IMAGE_REPOSITORY }}:${{ steps.set-properties.outputs.VERSION }}, ${{ vars.DOCKERHUB_IMAGE_REPOSITORY }}:${{ steps.set-properties.outputs.VERSION }}-${{ steps.set-properties.outputs.DATE }} + file: Dockerfile + push: true + provenance: mode=max + sbom: true + build-args: | + VERSION=${{ steps.set-properties.outputs.VERSION }} + - uses: mongodb-js/devtools-shared/actions/setup-bot-token@main + id: app-token + if: ${{ failure() }} + with: + app-id: ${{ vars.DEVTOOLS_BOT_APP_ID }} + private-key: ${{ secrets.DEVTOOLS_BOT_PRIVATE_KEY }} + - name: Create Issue + if: ${{ failure() }} + uses: imjohnbo/issue-bot@572eed14422c4d6ca37e870f97e7da209422f5bd + with: + token: ${{ steps.app-token.outputs.token }} + title: Release Failure for Docker Image ${{ steps.set-properties.outputs.VERSION }}-${{ steps.set-properties.outputs.DATE }} + body: See https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + labels: "docker, release_failure" diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index b016237f..5300316a 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -10,16 +10,18 @@ on: required: true default: "patch" +permissions: {} + jobs: create-pr: runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: mongodb-js/devtools-shared/actions/setup-bot-token@main id: app-token with: app-id: ${{ vars.DEVTOOLS_BOT_APP_ID }} private-key: ${{ secrets.DEVTOOLS_BOT_PRIVATE_KEY }} - - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: @@ -32,14 +34,20 @@ jobs: echo "NEW_VERSION=$(npm version ${{ inputs.version }} --no-git-tag-version)" >> $GITHUB_OUTPUT - name: Create release PR uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # 7.0.8 + id: create-pr with: - title: "Release v${{ steps.bump-version.outputs.NEW_VERSION }}" + title: "chore: release ${{ steps.bump-version.outputs.NEW_VERSION }}" token: ${{ steps.app-token.outputs.token }} - commit-message: "Bump version to v${{ steps.bump-version.outputs.NEW_VERSION }}" + commit-message: "chore: release ${{ steps.bump-version.outputs.NEW_VERSION }}" body: | - This PR bumps the package version to v${{ steps.bump-version.outputs.NEW_VERSION }}. + This PR bumps the package version to ${{ steps.bump-version.outputs.NEW_VERSION }}. Once merged, the new version will be published to npm. base: main - branch: release/v${{ steps.bump-version.outputs.NEW_VERSION }} + branch: chore_release_${{ steps.bump-version.outputs.NEW_VERSION }} author: "${{ steps.app-token.outputs.app-slug}}[bot] <${{ steps.app-token.outputs.app-email }}>" committer: "${{ steps.app-token.outputs.app-slug}}[bot] <${{ steps.app-token.outputs.app-email }}>" + - name: Set auto merge + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + gh pr merge ${{ steps.create-pr.outputs.pull-request-number }} --auto --squash diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 2742c649..e8964fa8 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -4,11 +4,11 @@ on: push: branches: - main -permissions: - contents: write + jobs: check: runs-on: ubuntu-latest + permissions: {} outputs: VERSION_EXISTS: ${{ steps.check-version.outputs.VERSION_EXISTS }} VERSION: ${{ steps.get-version.outputs.VERSION }} @@ -45,7 +45,10 @@ jobs: publish: runs-on: ubuntu-latest environment: Production - needs: check + permissions: + contents: write + needs: + - check if: needs.check.outputs.VERSION_EXISTS == 'false' steps: - uses: GitHubSecurityLab/actions-permissions/monitor@v1 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..fb2ec4d6 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,32 @@ +--- +name: "Stale issues and PRs handler" +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * *" + +permissions: read-all + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 + - uses: actions/stale@v9 + id: stale + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: | + This issue has gone 30 days without any activity and meets the project's definition of "stale". This will be auto-closed if there is no new activity over the next 30 days. If the issue is still relevant and active, you can simply comment with a "bump" to keep it open, or add the label "not_stale". Thanks for keeping our repository healthy! + stale-pr-message: | + This PR has gone 30 days without any activity and meets the project's definition of "stale". This will be auto-closed if there is no new activity over the next 30 days. If the issue is still relevant and active, you can simply comment with a "bump" to keep it open, or add the label "not_stale". Thanks for keeping our repository healthy! + stale-issue-label: "no-issue-activity" + stale-pr-label: "no-pr-activity" + days-before-stale: 30 + days-before-close: 30 + exempt-all-milestones: true + exempt-issue-labels: "not_stale" + exempt-pr-labels: "not_stale" diff --git a/.prettierrc.json b/.prettierrc.json index a8d4dcc3..1afbde18 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -27,7 +27,7 @@ } }, { - "files": "*.yaml", + "files": ["*.yaml", "*.yml"], "options": { "tabWidth": 2, "printWidth": 80 diff --git a/.smithery/Dockerfile b/.smithery/Dockerfile new file mode 100644 index 00000000..e2b469da --- /dev/null +++ b/.smithery/Dockerfile @@ -0,0 +1,30 @@ +# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile +# ----- Build Stage ----- +FROM node:lts-alpine AS builder +WORKDIR /app + +# Copy package and configuration +COPY ../package.json ../package-lock.json ../tsconfig.json ../tsconfig.build.json ./ + +# Copy source code +COPY ../src ./src + +# Install dependencies and build +RUN npm ci && npm run build + +# ----- Production Stage ----- +FROM node:lts-alpine + +# Copy built artifacts +COPY --from=builder /app/dist ./dist + +# Copy package.json for production install +COPY ../package.json ../package-lock.json ./ + +# Install only production dependencies +RUN npm ci --production --ignore-scripts + +# Expose no ports (stdio only) + +# Default command +CMD ["node", "dist/index.js"] diff --git a/.smithery/smithery.yaml b/.smithery/smithery.yaml new file mode 100644 index 00000000..e7de81be --- /dev/null +++ b/.smithery/smithery.yaml @@ -0,0 +1,63 @@ +# Smithery.ai configuration +build: + dockerfile: Dockerfile + dockerBuildPath: ../ +startCommand: + type: stdio + configSchema: + type: object + properties: + atlasClientId: + type: string + title: Atlas Client Id + description: Atlas API client ID for authentication. Required for running Atlas tools. + atlasClientSecret: + type: string + title: Atlas Client Secret + description: Atlas API client secret for authentication. Required for running Atlas tools. + connectionString: + type: string + title: MongoDB Connection string + description: MongoDB connection string for direct database connections. Optional, if not set, you'll need to call the `connect` tool before interacting with MongoDB data. + readOnly: + type: boolean + title: Read-only + description: When set to true, only allows read and metadata operation types, disabling create/update/delete operations. + default: false + exampleConfig: + atlasClientId: YOUR_ATLAS_CLIENT_ID + atlasClientSecret: YOUR_ATLAS_CLIENT_SECRET + connectionString: mongodb+srv://USERNAME:PASSWORD@YOUR_CLUSTER.mongodb.net + readOnly: true + + commandFunction: + # A function that produces the CLI command to start the MCP on stdio. + |- + (config) => { + const args = ['dist/index.js']; + if (config) { + if (config.atlasClientId) { + args.push('--apiClientId'); + args.push(config.atlasClientId); + } + + if (config.atlasClientSecret) { + args.push('--apiClientSecret'); + args.push(config.atlasClientSecret); + } + + if (config.readOnly) { + args.push('--readOnly'); + } + + if (config.connectionString) { + args.push('--connectionString'); + args.push(config.connectionString); + } + } + + return { + command: "node", + args + }; + } diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..e230623b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": ["firsttris.vscode-jest-runner", "orta.vscode-jest"], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index a55e49ac..969a92f2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "name": "Launch Program", "skipFiles": ["/**"], "program": "${workspaceFolder}/dist/index.js", - "preLaunchTask": "tsc: build - tsconfig.json", + "preLaunchTask": "tsc: build - tsconfig.build.json", "outFiles": ["${workspaceFolder}/dist/**/*.js"] } ] diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..c8c903bd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "jestrunner.jestCommand": "npm test --", + "jestrunner.debugOptions": { + "runtimeExecutable": "node", + "runtimeArgs": [ + "--experimental-vm-modules", + "node_modules/jest/bin/jest.js", + "--coverage" + ] + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9eb3620b..b35e5f4b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,7 +55,7 @@ This project implements a Model Context Protocol (MCP) server for MongoDB and Mo npm run inspect ``` -4. Commit your changes with a descriptive commit message +4. Commit your changes using [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) format. ## Adding tests to the MCP Server diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..05da379f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM node:22-alpine +ARG VERSION=latest +RUN addgroup -S mcp && adduser -S mcp -G mcp +RUN npm install -g mongodb-mcp-server@${VERSION} +USER mcp +WORKDIR /home/mcp +ENTRYPOINT ["mongodb-mcp-server"] +LABEL maintainer="MongoDB Inc " +LABEL description="MongoDB MCP Server" +LABEL version=${VERSION} diff --git a/README.md b/README.md index 8c625566..da193cf5 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,210 @@ -# MongoDB MCP Server +[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?logo=data:image/svg%2bxml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB2aWV3Qm94PSIwIDAgNDggNDgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiPjxwYXRoIGQ9Ik00NC45OTkgMTAuODd2MjYuMjFjMCAxLjAzLS41OSAxLjk3LTEuNTEgMi40Mi0yLjY4IDEuMjktOCAzLjg1LTguMzUgNC4wMS0uMTMuMDctLjM4LjItLjY3LjMxLjM1LS42LjUzLTEuMy41My0yLjAyVjYuMmMwLS43NS0uMi0xLjQ1LS41Ni0yLjA2LjA5LjA0LjE3LjA4LjI0LjExLjIuMSA1Ljk4IDIuODYgOC44IDQuMkM0NC40MDkgOC45IDQ0Ljk5OSA5Ljg0IDQ0Ljk5OSAxMC44N3pNNy40OTkgMjYuMDNjMS42IDEuNDYgMy40MyAzLjEzIDUuMzQgNC44NmwtNC42IDMuNWMtLjc3LjU3LTEuNzguNS0yLjU2LS4wNS0uNS0uMzYtMS44OS0xLjY1LTEuODktMS42NS0xLjAxLS44MS0xLjA2LTIuMzItLjExLTMuMTlDMy42NzkgMjkuNSA1LjE3OSAyOC4xMyA3LjQ5OSAyNi4wM3pNMzEuOTk5IDYuMnYxMC4xMWwtNy42MyA1LjgtNi44NS01LjIxYzQuOTgtNC41MyAxMC4wMS05LjExIDEyLjY1LTExLjUyQzMwLjg2OSA0Ljc0IDMxLjk5OSA1LjI1IDMxLjk5OSA2LjJ6TTMyIDQxLjc5OFYzMS42OUw4LjI0IDEzLjYxYy0uNzctLjU3LTEuNzgtLjUtMi41Ni4wNS0uNS4zNi0xLjg5IDEuNjUtMS44OSAxLjY1LTEuMDEuODEtMS4wNiAyLjMyLS4xMSAzLjE5IDAgMCAyMC4xNDUgMTguMzM4IDI2LjQ4NSAyNC4xMTZDMzAuODcxIDQzLjI2IDMyIDQyLjc1MyAzMiA0MS43OTh6Ii8+PC9zdmc+)](https://insiders.vscode.dev/redirect/mcp/install?name=mongodb&inputs=%5B%7B%22id%22%3A%22connection_string%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22MongoDB%20connection%20string%22%7D%5D&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22mongodb-mcp-server%22%5D%2C%22env%22%3A%7B%22MDB_MCP_CONNECTION_STRING%22%3A%22%24%7Binput%3Aconnection_string%7D%22%7D%7D) +[![Install in Cursor](https://img.shields.io/badge/Cursor-Install_Server-1e1e1e?logo=data:image/svg%2bxml;base64,PHN2ZyBoZWlnaHQ9IjFlbSIgc3R5bGU9ImZsZXg6bm9uZTtsaW5lLWhlaWdodDoxIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIxZW0iCiAgICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgPHRpdGxlPkN1cnNvcjwvdGl0bGU+CiAgICA8cGF0aCBkPSJNMTEuOTI1IDI0bDEwLjQyNS02LTEwLjQyNS02TDEuNSAxOGwxMC40MjUgNnoiCiAgICAgICAgZmlsbD0idXJsKCNsb2JlLWljb25zLWN1cnNvcnVuZGVmaW5lZC1maWxsLTApIj48L3BhdGg+CiAgICA8cGF0aCBkPSJNMjIuMzUgMThWNkwxMS45MjUgMHYxMmwxMC40MjUgNnoiIGZpbGw9InVybCgjbG9iZS1pY29ucy1jdXJzb3J1bmRlZmluZWQtZmlsbC0xKSI+PC9wYXRoPgogICAgPHBhdGggZD0iTTExLjkyNSAwTDEuNSA2djEybDEwLjQyNS02VjB6IiBmaWxsPSJ1cmwoI2xvYmUtaWNvbnMtY3Vyc29ydW5kZWZpbmVkLWZpbGwtMikiPjwvcGF0aD4KICAgIDxwYXRoIGQ9Ik0yMi4zNSA2TDExLjkyNSAyNFYxMkwyMi4zNSA2eiIgZmlsbD0iIzU1NSI+PC9wYXRoPgogICAgPHBhdGggZD0iTTIyLjM1IDZsLTEwLjQyNSA2TDEuNSA2aDIwLjg1eiIgZmlsbD0iI2ZmZiI+PC9wYXRoPgogICAgPGRlZnM+CiAgICAgICAgPGxpbmVhckdyYWRpZW50IGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBpZD0ibG9iZS1pY29ucy1jdXJzb3J1bmRlZmluZWQtZmlsbC0wIgogICAgICAgICAgICB4MT0iMTEuOTI1IiB4Mj0iMTEuOTI1IiB5MT0iMTIiIHkyPSIyNCI+CiAgICAgICAgICAgIDxzdG9wIG9mZnNldD0iLjE2IiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9Ii4zOSI+PC9zdG9wPgogICAgICAgICAgICA8c3RvcCBvZmZzZXQ9Ii42NTgiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjgiPjwvc3RvcD4KICAgICAgICA8L2xpbmVhckdyYWRpZW50PgogICAgICAgIDxsaW5lYXJHcmFkaWVudCBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9ImxvYmUtaWNvbnMtY3Vyc29ydW5kZWZpbmVkLWZpbGwtMSIKICAgICAgICAgICAgeDE9IjIyLjM1IiB4Mj0iMTEuOTI1IiB5MT0iNi4wMzciIHkyPSIxMi4xNSI+CiAgICAgICAgICAgIDxzdG9wIG9mZnNldD0iLjE4MiIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuMzEiPjwvc3RvcD4KICAgICAgICAgICAgPHN0b3Agb2Zmc2V0PSIuNzE1IiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9IjAiPjwvc3RvcD4KICAgICAgICA8L2xpbmVhckdyYWRpZW50PgogICAgICAgIDxsaW5lYXJHcmFkaWVudCBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9ImxvYmUtaWNvbnMtY3Vyc29ydW5kZWZpbmVkLWZpbGwtMiIKICAgICAgICAgICAgeDE9IjExLjkyNSIgeDI9IjEuNSIgeTE9IjAiIHkyPSIxOCI+CiAgICAgICAgICAgIDxzdG9wIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjYiPjwvc3RvcD4KICAgICAgICAgICAgPHN0b3Agb2Zmc2V0PSIuNjY3IiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9Ii4yMiI+PC9zdG9wPgogICAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8L2RlZnM+Cjwvc3ZnPgo=)](https://cursor.com/install-mcp?name=MongoDB&config=eyJjb21tYW5kIjoibnB4IC15IG1vbmdvZGItbWNwLXNlcnZlciJ9) +[![View on Smithery](https://smithery.ai/badge/@mongodb-js/mongodb-mcp-server)](https://smithery.ai/server/@mongodb-js/mongodb-mcp-server) -A Model Context Protocol server for interacting with MongoDB Atlas. This project implements a Model Context Protocol (MCP) server enabling AI assistants to interact with MongoDB Atlas resources through natural language. +# MongoDB MCP Server -> [!CAUTION] -> Do not use this in production. This is a work in progress and is not intended for production use. It is meant for demonstration purposes only. +A Model Context Protocol server for interacting with MongoDB Databases and MongoDB Atlas. ## 📚 Table of Contents - [🚀 Getting Started](#getting-started) - [Prerequisites](#prerequisites) - - [Installation](#installation) - - [VSCode](#vscode) - - [Claude Desktop](#claude) + - [Setup](#setup) + - [Quick Start](#quick-start) - [🛠️ Supported Tools](#supported-tools) - - [Tool List](#tool-list) + - [MongoDB Atlas Tools](#mongodb-atlas-tools) + - [MongoDB Database Tools](#mongodb-database-tools) - [⚙️ Configuration](#configuration) - [Configuration Options](#configuration-options) - [Atlas API Access](#atlas-api-access) - [Configuration Methods](#configuration-methods) -- [👩‍💻 Client Integration](#client-integration) - - [VSCode](#vscode) - - [Claude](#claude) + - [Environment Variables](#environment-variables) + - [Command-Line Arguments](#command-line-arguments) + - [MCP Client Configuration](#mcp-configuration-file-examples) - [🤝 Contributing](#contributing) ## Prerequisites -- Node.js (v20 or later) -- MongoDB Atlas account +- Node.js (v20.10.0 or later) + +```shell +node -v +``` -## Installation +- A MongoDB connection string or Atlas API credentials, **_the Server will not start unless configured_**. + - **_Service Accounts Atlas API credentials_** are required to use the Atlas tools. You can create a service account in MongoDB Atlas and use its credentials for authentication. See [Atlas API Access](#atlas-api-access) for more details. + - If you have a MongoDB connection string, you can use it directly to connect to your MongoDB instance. -### VSCode +## Setup -Prerequisites: +### Quick Start -- Node.js v20.x +> **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. -Step 1: Add the mcp server to VSCode configuration +Most MCP clients require a configuration file to be created or modified to add the MCP server. -- Press `Cmd + Shift + P` and type `MCP: Add MCP Server` and select it. -- Select command (Stdio). -- Input command `npx -y mongodb-mcp-server`. -- Choose between user / workspace -- Add arguments to the file +Note: The configuration file syntax can be different across clients. Please refer to the following links for the latest expected syntax: -Note: the file should look like: +- **Windsurf**:https://docs.windsurf.com/windsurf/mcp +- **VSCode**: https://code.visualstudio.com/docs/copilot/chat/mcp-servers +- **Claude Desktop**: https://modelcontextprotocol.io/quickstart/user +- **Cursor**: https://docs.cursor.com/context/model-context-protocol +#### Option 1: Connection String args + +You can pass your connection string via args, make sure to use a valid username and password. + +```json +{ + "mcpServers": { + "MongoDB": { + "command": "npx", + "args": [ + "-y", + "mongodb-mcp-server", + "--connectionString", + "mongodb://localhost:27017/myDatabase" + ] + } + } +} ``` + +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 + +Use your Atlas API Service Accounts credentials. Must follow all the steps in [Atlas API Access](#atlas-api-access) section. + +```json { - "servers": { - "MongoDB": { - "type": "stdio", - "command": "npx", - "args": [ - "-y", - "mongodb-mcp-server" - ] - } + "mcpServers": { + "MongoDB": { + "command": "npx", + "args": [ + "-y", + "mongodb-mcp-server", + "--apiClientId", + "your-atlas-service-accounts-client-id", + "--apiClientSecret", + "your-atlas-service-accounts-client-secret" + ] } + } } ``` -Notes: You can configure the server with atlas access, make sure to follow configuration section for more details. +#### Option 3: Standalone Service using command arguments + +Start Server using npx command: + +```shell + npx -y mongodb-mcp-server --apiClientId="your-atlas-service-accounts-client-id" --apiClientSecret="your-atlas-service-accounts-client-secret" +``` + +- 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 + +```shell + npx -y mongodb-mcp-server +``` -Step 2: Try talking to github copilot +You can use environment variables in the config file or set them and run the server via npx. -- Can you connect to my mongodb instance? +- 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) -### Claude Desktop +#### Option 5: Using Docker -Step 1: Install claude and login +You can run the MongoDB MCP Server in a Docker container, which provides isolation and doesn't require a local Node.js installation. -Note: follow instructions at https://claude.ai/download +#### Run with Environment Variables -Step 2: Launch Claude Settings -> Developer -> Edit Config +You may provide either a MongoDB connection string OR Atlas API credentials: -Paste the mcp server configuration into the file +##### Option A: No configuration + +```shell +docker run --rm -i \ + mongodb/mongodb-mcp-server:latest +``` + +##### Option B: With MongoDB connection string + +```shell +docker run --rm -i \ + -e MDB_MCP_CONNECTION_STRING="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" \ + mongodb/mongodb-mcp-server:latest +``` + +##### Option C: With Atlas API credentials + +```shell +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" \ + mongodb/mongodb-mcp-server:latest +``` + +##### Docker in MCP Configuration File + +Without options: ```json { "mcpServers": { "MongoDB": { - "command": "npx", - "args": ["-y", "mongodb-mcp-server"] + "command": "docker", + "args": ["run", "--rm", "-i", "mongodb/mongodb-mcp-server:latest"] + } + } +} +``` + +With connection string: + +```json +{ + "mcpServers": { + "MongoDB": { + "command": "docker", + "args": [ + "run", + "--rm", + "-i", + "-e", + "MDB_MCP_CONNECTION_STRING=mongodb+srv://username:password@cluster.mongodb.net/myDatabase", + "mongodb/mongodb-mcp-server:latest" + ] } } } ``` -Step 3: Close and Relaunch Claude Desktop and click on the hammer icon, the MongoDB MCP server should be detected. +With Atlas API credentials: -You may experiment asking `Can you connect to my mongodb instance?`. +```json +{ + "mcpServers": { + "MongoDB": { + "command": "docker", + "args": [ + "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", + "mongodb/mongodb-mcp-server:latest" + ] + } + } +} +``` ## 🛠️ Supported Tools @@ -104,10 +218,12 @@ You may experiment asking `Can you connect to my mongodb instance?`. - `atlas-list-clusters` - Lists MongoDB Atlas clusters - `atlas-inspect-cluster` - Inspect a specific MongoDB Atlas cluster - `atlas-create-free-cluster` - Create a free MongoDB Atlas cluster +- `atlas-connect-cluster` - Connects to MongoDB Atlas cluster - `atlas-inspect-access-list` - Inspect IP/CIDR ranges with access to MongoDB Atlas clusters - `atlas-create-access-list` - Configure IP/CIDR access list for MongoDB Atlas clusters - `atlas-list-db-users` - List MongoDB Atlas database users -- `atlas-create-db-user` - List MongoDB Atlas database users +- `atlas-create-db-user` - Creates a MongoDB Atlas database user +- `atlas-list-alerts` - List MongoDB Atlas Alerts for a Project NOTE: atlas tools are only available when you set credentials on [configuration](#configuration) section. @@ -143,15 +259,17 @@ The MongoDB MCP Server can be configured using multiple methods, with the follow ### Configuration Options -| Option | Description | -| ------------------ | --------------------------------------------------------------------------------------------------------------------- | -| `apiClientId` | Atlas API client ID for authentication | -| `apiClientSecret` | Atlas API client secret for authentication | -| `connectionString` | MongoDB connection string for direct database connections (optional users may choose to inform it on every tool call) | -| `logPath` | Folder to store logs | -| `disabledTools` | An array of tool names, operation types, and/or categories of tools that will be disabled. | +| Option | Description | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `apiClientId` | Atlas API client ID for authentication. Required for running Atlas tools. | +| `apiClientSecret` | Atlas API client secret for authentication. Required for running Atlas tools. | +| `connectionString` | MongoDB connection string for direct database connections. Optional, if not set, you'll need to call the `connect` tool before interacting with MongoDB data. | +| `logPath` | Folder to store logs. | +| `disabledTools` | An array of tool names, operation types, and/or categories of tools that will be disabled. | +| `readOnly` | When set to true, only allows read and metadata operation types, disabling create/update/delete operations. | +| `telemetry` | When set to disabled, disables telemetry collection. | -#### `logPath` +#### Log Path Default log location is as follows: @@ -181,31 +299,79 @@ Operation types: - `read` - Tools that read resources, such as find, aggregate, list clusters, etc. - `metadata` - Tools that read metadata, such as list databases, list collections, collection schema, etc. +#### Read-Only Mode + +The `readOnly` configuration option allows you to restrict the MCP server to only use tools with "read" and "metadata" operation types. When enabled, all tools that have "create", "update" or "delete" operation types will not be registered with the server. + +This is useful for scenarios where you want to provide access to MongoDB data for analysis without allowing any modifications to the data or infrastructure. + +You can enable read-only mode using: + +- **Environment variable**: `export MDB_MCP_READ_ONLY=true` +- **Command-line argument**: `--readOnly` + +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. + +#### Telemetry + +The `telemetry` configuration option allows you to disable telemetry collection. When enabled, the MCP server will collect usage data and send it to MongoDB. + +You can disable telemetry using: + +- **Environment variable**: `export MDB_MCP_TELEMETRY=disabled` +- **Command-line argument**: `--telemetry disabled` +- **DO_NOT_TRACK environment variable**: `export DO_NOT_TRACK=1` + ### Atlas API Access To use the Atlas API tools, you'll need to create a service account in MongoDB Atlas: +> **ℹ️ Note:** For a detailed breakdown of the minimum required permissions for each Atlas operation, see the [Atlas API Permissions](#atlas-api-permissions) section below. + 1. **Create a Service Account:** - Log in to MongoDB Atlas at [cloud.mongodb.com](https://cloud.mongodb.com) - Navigate to Access Manager > Organization Access - Click Add New > Applications > Service Accounts - Enter name, description and expiration for your service account (e.g., "MCP, MCP Server Access, 7 days") - - Select appropriate permissions (for full access, use Organization Owner) + - **Assign only the minimum permissions needed for your use case.** + - See [Atlas API Permissions](#atlas-api-permissions) for details. - Click "Create" +To learn more about Service Accounts, check the [MongoDB Atlas documentation](https://www.mongodb.com/docs/atlas/api/service-accounts-overview/). + 2. **Save Client Credentials:** - After creation, you'll be shown the Client ID and Client Secret - **Important:** Copy and save the Client Secret immediately as it won't be displayed again -3. **Add Access List Entry (Optional but recommended):** +3. **Add Access List Entry:** - Add your IP address to the API access list 4. **Configure the MCP Server:** - Use one of the configuration methods below to set your `apiClientId` and `apiClientSecret` +### Atlas API Permissions + +> **Security Warning:** Granting the Organization Owner role is rarely necessary and can be a security risk. Assign only the minimum permissions needed for your use case. + +#### Quick Reference: Required roles per operation + +| What you want to do | Safest Role to Assign (where) | +| ------------------------------------ | --------------------------------------- | +| List orgs/projects | Org Member or Org Read Only (Org) | +| Create new projects | Org Project Creator (Org) | +| View clusters/databases in a project | Project Read Only (Project) | +| Create/manage clusters in a project | Project Cluster Manager (Project) | +| Manage project access lists | Project IP Access List Admin (Project) | +| Manage database users | Project Database Access Admin (Project) | + +- **Prefer project-level roles** for most operations. Assign only to the specific projects you need to manage or view. +- **Avoid Organization Owner** unless you require full administrative control over all projects and settings in the organization. + +For a full list of roles and their privileges, see the [Atlas User Roles documentation](https://www.mongodb.com/docs/atlas/reference/user-roles/#service-user-roles). + ### Configuration Methods #### Environment Variables @@ -213,14 +379,50 @@ To use the Atlas API tools, you'll need to create a service account in MongoDB A Set environment variables with the prefix `MDB_MCP_` followed by the option name in uppercase with underscores: ```shell -# Set Atlas API credentials -export MDB_MCP_API_CLIENT_ID="your-atlas-client-id" -export MDB_MCP_API_CLIENT_SECRET="your-atlas-client-secret" +# 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" # Set a custom MongoDB connection string export MDB_MCP_CONNECTION_STRING="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" export MDB_MCP_LOG_PATH="/path/to/logs" + +``` + +#### MCP configuration file examples + +##### Connection String with environment variables + +```json +{ + "mcpServers": { + "MongoDB": { + "command": "npx", + "args": ["-y", "mongodb-mcp-server"], + "env": { + "MDB_MCP_CONNECTION_STRING": "mongodb+srv://username:password@cluster.mongodb.net/myDatabase" + } + } + } +} +``` + +##### Atlas API credentials with environment variables + +```json +{ + "mcpServers": { + "MongoDB": { + "command": "npx", + "args": ["-y", "mongodb-mcp-server"], + "env": { + "MDB_MCP_API_CLIENT_ID": "your-atlas-service-accounts-client-id", + "MDB_MCP_API_CLIENT_SECRET": "your-atlas-service-accounts-client-secret" + } + } + } +} ``` #### Command-Line Arguments @@ -228,7 +430,47 @@ export MDB_MCP_LOG_PATH="/path/to/logs" Pass configuration options as command-line arguments when starting the server: ```shell -npx -y mongodb-mcp-server --apiClientId="your-atlas-client-id" --apiClientSecret="your-atlas-client-secret" --connectionString="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" --logPath=/path/to/logs +npx -y mongodb-mcp-server --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 +``` + +#### MCP configuration file examples + +##### Connection String with command-line arguments + +```json +{ + "mcpServers": { + "MongoDB": { + "command": "npx", + "args": [ + "-y", + "mongodb-mcp-server", + "--connectionString", + "mongodb+srv://username:password@cluster.mongodb.net/myDatabase" + ] + } + } +} +``` + +##### Atlas API credentials with command-line arguments + +```json +{ + "mcpServers": { + "MongoDB": { + "command": "npx", + "args": [ + "-y", + "mongodb-mcp-server", + "--apiClientId", + "your-atlas-service-accounts-client-id", + "--apiClientSecret", + "your-atlas-service-accounts-client-secret" + ] + } + } +} ``` ## 🤝 Contributing diff --git a/eslint.config.js b/eslint.config.js index b6263450..e7059fc5 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,18 +2,34 @@ import { defineConfig, globalIgnores } from "eslint/config"; import js from "@eslint/js"; import globals from "globals"; import tseslint from "typescript-eslint"; -import eslintConfigPrettier from "eslint-config-prettier/flat"; +import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; +import jestPlugin from "eslint-plugin-jest"; -const files = ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.test.ts", "eslint.config.js", "jest.config.js"]; +const testFiles = ["tests/**/*.test.ts", "tests/**/*.ts"]; + +const files = [...testFiles, "src/**/*.ts", "scripts/**/*.ts"]; export default defineConfig([ { files, plugins: { js }, extends: ["js/recommended"] }, { files, languageOptions: { globals: globals.node } }, + { + files: testFiles, + plugins: { + jest: jestPlugin, + }, + languageOptions: { + globals: { + ...globals.node, + ...jestPlugin.environments.globals.globals, + }, + }, + }, tseslint.configs.recommendedTypeChecked, { + files, languageOptions: { parserOptions: { - projectService: true, + project: "./tsconfig.json", tsconfigRootDir: import.meta.dirname, }, }, @@ -25,11 +41,15 @@ export default defineConfig([ "@typescript-eslint/no-non-null-assertion": "error", }, }, - // Ignore features specific to TypeScript resolved rules - tseslint.config({ - // TODO: Configure tests and scripts to work with this. - ignores: ["eslint.config.js", "jest.config.js", "tests/**/*.ts", "scripts/**/*.ts"], - }), - globalIgnores(["node_modules", "dist", "src/common/atlas/openapi.d.ts", "coverage"]), - eslintConfigPrettier, + globalIgnores([ + "node_modules", + "dist", + "src/common/atlas/openapi.d.ts", + "coverage", + "global.d.ts", + "eslint.config.js", + "jest.config.cjs", + "src/types/*.d.ts", + ]), + eslintPluginPrettierRecommended, ]); diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 00000000..3b47093f --- /dev/null +++ b/global.d.ts @@ -0,0 +1 @@ +import "jest-extended"; diff --git a/jest.config.js b/jest.config.cjs similarity index 85% rename from jest.config.js rename to jest.config.cjs index 5b06aaed..f9a34b53 100644 --- a/jest.config.js +++ b/jest.config.cjs @@ -1,5 +1,5 @@ /** @type {import('ts-jest').JestConfigWithTsJest} **/ -export default { +module.exports = { preset: "ts-jest/presets/default-esm", testEnvironment: "node", extensionsToTreatAsEsm: [".ts"], @@ -12,7 +12,7 @@ export default { "ts-jest", { useESM: true, - tsconfig: "tsconfig.jest.json", // Use specific tsconfig file for Jest + tsconfig: "tsconfig.jest.json", }, ], }, diff --git a/package-lock.json b/package-lock.json index 85d46171..041de7d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,26 +1,29 @@ { "name": "mongodb-mcp-server", - "version": "0.0.3", + "version": "0.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mongodb-mcp-server", - "version": "0.0.3", + "version": "0.1.2", "license": "Apache-2.0", "dependencies": { - "@modelcontextprotocol/sdk": "^1.8.0", + "@modelcontextprotocol/sdk": "^1.11.2", + "@mongodb-js/device-id": "^0.2.1", "@mongodb-js/devtools-connect": "^3.7.2", "@mongosh/service-provider-node-driver": "^3.6.0", "bson": "^6.10.3", "lru-cache": "^11.1.0", "mongodb": "^6.15.0", + "mongodb-connection-string-url": "^3.0.2", "mongodb-log-writer": "^2.4.1", "mongodb-redact": "^1.1.6", "mongodb-schema": "^12.6.2", - "openapi-fetch": "^0.13.5", + "node-machine-id": "1.1.12", + "openapi-fetch": "^0.14.0", "simple-oauth2": "^5.1.0", - "yargs-parser": "^21.1.1", + "yargs-parser": "^22.0.0", "zod": "^3.24.2" }, "bin": { @@ -28,21 +31,22 @@ }, "devDependencies": { "@eslint/js": "^9.24.0", - "@jest/globals": "^29.7.0", - "@modelcontextprotocol/inspector": "^0.8.2", + "@jest/globals": "^30.0.0", + "@modelcontextprotocol/inspector": "^0.14.0", "@redocly/cli": "^1.34.2", "@types/jest": "^29.5.14", "@types/node": "^22.14.0", "@types/simple-oauth2": "^5.0.7", "@types/yargs-parser": "^21.0.3", "eslint": "^9.24.0", - "eslint-config-prettier": "^10.1.1", + "eslint-config-prettier": "^10.1.2", + "eslint-plugin-jest": "^28.11.0", + "eslint-plugin-prettier": "^5.2.6", "globals": "^16.0.0", "jest": "^29.7.0", "jest-environment-node": "^29.7.0", - "jest-extended": "^4.0.2", + "jest-extended": "^6.0.0", "mongodb-runner": "^5.8.2", - "native-machine-id": "^0.0.8", "openapi-types": "^12.1.3", "openapi-typescript": "^7.6.1", "prettier": "^3.5.3", @@ -53,7 +57,7 @@ "yaml": "^2.7.1" }, "engines": { - "node": ">=20.0.0" + "node": ">=20.10.0" } }, "node_modules/@ampproject/remapping": { @@ -196,45 +200,45 @@ } }, "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.787.0.tgz", - "integrity": "sha512-7v6nywZ5wcQxX7qdZ5M1ld15QdkzLU6fAKiEqbvJKu4dM8cFW6As+DbS990Mg46pp1xM/yvme+51xZDTfTfJZA==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.798.0.tgz", + "integrity": "sha512-36vZT6hyYRJRNGBdxintkIZwq8hWsMCTKmi6ZqtcV4Jt65yNjQZTXuGui6/NdGj7KAmOh/RoyTpJzWKwIA5sTA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.775.0", - "@aws-sdk/credential-provider-node": "3.787.0", + "@aws-sdk/core": "3.798.0", + "@aws-sdk/credential-provider-node": "3.798.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", - "@aws-sdk/middleware-user-agent": "3.787.0", + "@aws-sdk/middleware-user-agent": "3.798.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.787.0", "@aws-sdk/util-user-agent-browser": "3.775.0", - "@aws-sdk/util-user-agent-node": "3.787.0", + "@aws-sdk/util-user-agent-node": "3.798.0", "@smithy/config-resolver": "^4.1.0", - "@smithy/core": "^3.2.0", + "@smithy/core": "^3.3.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", - "@smithy/middleware-endpoint": "^4.1.0", - "@smithy/middleware-retry": "^4.1.0", + "@smithy/middleware-endpoint": "^4.1.1", + "@smithy/middleware-retry": "^4.1.1", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.1", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@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.8", - "@smithy/util-defaults-mode-node": "^4.0.8", + "@smithy/util-defaults-mode-browser": "^4.0.9", + "@smithy/util-defaults-mode-node": "^4.0.9", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", @@ -246,44 +250,44 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.787.0.tgz", - "integrity": "sha512-L8R+Mh258G0DC73ktpSVrG4TT9i2vmDLecARTDR/4q5sRivdDQSL5bUp3LKcK80Bx+FRw3UETIlX6mYMLL9PJQ==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.798.0.tgz", + "integrity": "sha512-Si4W7kFflNXC48lr05n2Fc5nrD6whbfgR7c5/7hYSXP52DOqy2kMle+bZx5EkmQ/e/5nAPW0DS4ABeLprVSghw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.775.0", + "@aws-sdk/core": "3.798.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", - "@aws-sdk/middleware-user-agent": "3.787.0", + "@aws-sdk/middleware-user-agent": "3.798.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.787.0", "@aws-sdk/util-user-agent-browser": "3.775.0", - "@aws-sdk/util-user-agent-node": "3.787.0", + "@aws-sdk/util-user-agent-node": "3.798.0", "@smithy/config-resolver": "^4.1.0", - "@smithy/core": "^3.2.0", + "@smithy/core": "^3.3.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", - "@smithy/middleware-endpoint": "^4.1.0", - "@smithy/middleware-retry": "^4.1.0", + "@smithy/middleware-endpoint": "^4.1.1", + "@smithy/middleware-retry": "^4.1.1", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.1", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@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.8", - "@smithy/util-defaults-mode-node": "^4.0.8", + "@smithy/util-defaults-mode-browser": "^4.0.9", + "@smithy/util-defaults-mode-node": "^4.0.9", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", @@ -295,18 +299,18 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.775.0.tgz", - "integrity": "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.798.0.tgz", + "integrity": "sha512-hITxDE4pVkeJqz0LXjQRDgR+noxJ5oOxG38fgmQXjPXsdwVKnNIiMJ5S2WFMVSszU7ebGSyHdPHENQKu6TReVA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.775.0", - "@smithy/core": "^3.2.0", + "@smithy/core": "^3.3.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", - "@smithy/signature-v4": "^5.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/signature-v4": "^5.1.0", + "@smithy/smithy-client": "^4.2.1", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", @@ -317,12 +321,12 @@ } }, "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.787.0.tgz", - "integrity": "sha512-nF5XjgvZHFuyttOeTjMgfEsg6slZPQ6uI34yzq12Kq4icFgcD4bQsijnQClMN7A0u5qR8Ad8kume4b7+I2++Ig==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.798.0.tgz", + "integrity": "sha512-uZ204ov8uQFRYNp8NGGP/oVBkE+PDzyFBlCpluBHsnOUZtVL2QJRcBHZqYWbHfoJ8x+WuD6VNU2pG4kxzQCSyw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-cognito-identity": "3.787.0", + "@aws-sdk/client-cognito-identity": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", @@ -333,12 +337,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.775.0.tgz", - "integrity": "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.798.0.tgz", + "integrity": "sha512-EsfzTEeoaHY1E+g3S6AmC3bF6euZN5SrLcLh5Oxhx5q2qjWUsKEK0fwek+jlt2GH7zB3F9IArV4z+8CsDQdKYw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", + "@aws-sdk/core": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", @@ -349,18 +353,18 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.775.0.tgz", - "integrity": "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.798.0.tgz", + "integrity": "sha512-bw5TmcJqpBVQlXzkL63545iHQ9mxwQeXTS/rgUQ5rmNNS3yiGDekVZOLXo/Gs4wmt2/59UN/sWIRFxvxDpMQEg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", + "@aws-sdk/core": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.1", "@smithy/types": "^4.2.0", "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" @@ -370,18 +374,18 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.787.0.tgz", - "integrity": "sha512-hc2taRoDlXn2uuNuHWDJljVWYrp3r9JF1a/8XmOAZhVUNY+ImeeStylHXhXXKEA4JOjW+5PdJj0f1UDkVCHJiQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.775.0", - "@aws-sdk/credential-provider-env": "3.775.0", - "@aws-sdk/credential-provider-http": "3.775.0", - "@aws-sdk/credential-provider-process": "3.775.0", - "@aws-sdk/credential-provider-sso": "3.787.0", - "@aws-sdk/credential-provider-web-identity": "3.787.0", - "@aws-sdk/nested-clients": "3.787.0", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.798.0.tgz", + "integrity": "sha512-zqWwKhhdf5CVRL6+4vNNTZVHWH9OiiwUWA3ka44jJaAMBRbbryjRedzwkWbgDaL1EbfTbcBZTYzE7N/vK7UUVA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.798.0", + "@aws-sdk/credential-provider-env": "3.798.0", + "@aws-sdk/credential-provider-http": "3.798.0", + "@aws-sdk/credential-provider-process": "3.798.0", + "@aws-sdk/credential-provider-sso": "3.798.0", + "@aws-sdk/credential-provider-web-identity": "3.798.0", + "@aws-sdk/nested-clients": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", @@ -394,17 +398,17 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.787.0.tgz", - "integrity": "sha512-JioVi44B1vDMaK2CdzqimwvJD3uzvzbQhaEWXsGMBcMcNHajXAXf08EF50JG3ZhLrhhUsT1ObXpbTaPINOhh+g==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.798.0.tgz", + "integrity": "sha512-Mrhl4wS4lMpuw2NCga5/rtQehNfyRs8NUHfvrLK5bZvJbjanrh8QtdRVhrAjw71OwFh3GK49QMByGkUssALJ+g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.775.0", - "@aws-sdk/credential-provider-http": "3.775.0", - "@aws-sdk/credential-provider-ini": "3.787.0", - "@aws-sdk/credential-provider-process": "3.775.0", - "@aws-sdk/credential-provider-sso": "3.787.0", - "@aws-sdk/credential-provider-web-identity": "3.787.0", + "@aws-sdk/credential-provider-env": "3.798.0", + "@aws-sdk/credential-provider-http": "3.798.0", + "@aws-sdk/credential-provider-ini": "3.798.0", + "@aws-sdk/credential-provider-process": "3.798.0", + "@aws-sdk/credential-provider-sso": "3.798.0", + "@aws-sdk/credential-provider-web-identity": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", @@ -417,12 +421,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.775.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.775.0.tgz", - "integrity": "sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.798.0.tgz", + "integrity": "sha512-BbRq8bhCHC94OTRIg5edgGTaWUzBH0h/IZJZ0vERle8A9nfl+5jUplvC8cvh3/8cNgHIRXj5HzlDjeSVe9dySg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", + "@aws-sdk/core": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", @@ -434,14 +438,14 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.787.0.tgz", - "integrity": "sha512-fHc08bsvwm4+dEMEQKnQ7c1irEQmmxbgS+Fq41y09pPvPh31nAhoMcjBSTWAaPHvvsRbTYvmP4Mf12ZGr8/nfg==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.798.0.tgz", + "integrity": "sha512-MLpQRb7xkqI9w0slEA76QiHGzM0PDMcpVcQG0wFHrpLKkQYjYlD9H3VfxdYGUh+FPOaR1fFpRZb18Gz9MR/2eQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.787.0", - "@aws-sdk/core": "3.775.0", - "@aws-sdk/token-providers": "3.787.0", + "@aws-sdk/client-sso": "3.798.0", + "@aws-sdk/core": "3.798.0", + "@aws-sdk/token-providers": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", @@ -453,13 +457,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.787.0.tgz", - "integrity": "sha512-SobmCwNbk6TfEsF283mZPQEI5vV2j6eY5tOCj8Er4Lzraxu9fBPADV+Bib2A8F6jlB1lMPJzOuDCbEasSt/RIw==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.798.0.tgz", + "integrity": "sha512-OWBDy/ZiC0pxLzp1Nhah5jxDZ/onLTjouIVGPyc9E8/KzUJxqQbR6fk43VqhpYdVp/S7yDDbaOpO072RRZJQrw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", - "@aws-sdk/nested-clients": "3.787.0", + "@aws-sdk/core": "3.798.0", + "@aws-sdk/nested-clients": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", @@ -470,25 +474,25 @@ } }, "node_modules/@aws-sdk/credential-providers": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.787.0.tgz", - "integrity": "sha512-kR3RtI7drOc9pho13vWbUC2Bvrx9A0G4iizBDGmTs08NOdg4w3c1I4kdLG9tyPiIMeVnH+wYrsli5CM7xIfqiA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.787.0", - "@aws-sdk/core": "3.775.0", - "@aws-sdk/credential-provider-cognito-identity": "3.787.0", - "@aws-sdk/credential-provider-env": "3.775.0", - "@aws-sdk/credential-provider-http": "3.775.0", - "@aws-sdk/credential-provider-ini": "3.787.0", - "@aws-sdk/credential-provider-node": "3.787.0", - "@aws-sdk/credential-provider-process": "3.775.0", - "@aws-sdk/credential-provider-sso": "3.787.0", - "@aws-sdk/credential-provider-web-identity": "3.787.0", - "@aws-sdk/nested-clients": "3.787.0", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.798.0.tgz", + "integrity": "sha512-xHEroRdp01YSZ6zi/SrYLoN2faUfkZqgA1kvcv/z+3kl5R6OVNBvMsfoO/jYtuovsw/ve7zDg/62ezAZrdbV3g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.798.0", + "@aws-sdk/core": "3.798.0", + "@aws-sdk/credential-provider-cognito-identity": "3.798.0", + "@aws-sdk/credential-provider-env": "3.798.0", + "@aws-sdk/credential-provider-http": "3.798.0", + "@aws-sdk/credential-provider-ini": "3.798.0", + "@aws-sdk/credential-provider-node": "3.798.0", + "@aws-sdk/credential-provider-process": "3.798.0", + "@aws-sdk/credential-provider-sso": "3.798.0", + "@aws-sdk/credential-provider-web-identity": "3.798.0", + "@aws-sdk/nested-clients": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/config-resolver": "^4.1.0", - "@smithy/core": "^3.2.0", + "@smithy/core": "^3.3.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/property-provider": "^4.0.2", @@ -544,15 +548,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.787.0.tgz", - "integrity": "sha512-Lnfj8SmPLYtrDFthNIaNj66zZsBCam+E4XiUDr55DIHTGstH6qZ/q6vg0GfbukxwSmUcGMwSR4Qbn8rb8yd77g==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.798.0.tgz", + "integrity": "sha512-nb3YvLokpu/2meKVH5hGVLNg+hz3IyFCESEJW+SpK7bW/SfaKpukGY1lqwqbf+edl+s20MRXeK/by1rvBChixQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.775.0", + "@aws-sdk/core": "3.798.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.787.0", - "@smithy/core": "^3.2.0", + "@smithy/core": "^3.3.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" @@ -562,44 +566,44 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.787.0.tgz", - "integrity": "sha512-xk03q1xpKNHgbuo+trEf1dFrI239kuMmjKKsqLEsHlAZbuFq4yRGMlHBrVMnKYOPBhVFDS/VineM991XI52fKg==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.798.0.tgz", + "integrity": "sha512-14iBJgg2Qqf74IeUY+z1nP5GIJIBZj8lv9mdpXrHlK8k+FcMXjpHg/B+JguSMhb2sbLeb5N0H8HLJGIRNALVWw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.775.0", + "@aws-sdk/core": "3.798.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", - "@aws-sdk/middleware-user-agent": "3.787.0", + "@aws-sdk/middleware-user-agent": "3.798.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.787.0", "@aws-sdk/util-user-agent-browser": "3.775.0", - "@aws-sdk/util-user-agent-node": "3.787.0", + "@aws-sdk/util-user-agent-node": "3.798.0", "@smithy/config-resolver": "^4.1.0", - "@smithy/core": "^3.2.0", + "@smithy/core": "^3.3.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", - "@smithy/middleware-endpoint": "^4.1.0", - "@smithy/middleware-retry": "^4.1.0", + "@smithy/middleware-endpoint": "^4.1.1", + "@smithy/middleware-retry": "^4.1.1", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.1", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@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.8", - "@smithy/util-defaults-mode-node": "^4.0.8", + "@smithy/util-defaults-mode-browser": "^4.0.9", + "@smithy/util-defaults-mode-node": "^4.0.9", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", @@ -628,12 +632,12 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.787.0.tgz", - "integrity": "sha512-d7/NIqxq308Zg0RPMNrmn0QvzniL4Hx8Qdwzr6YZWLYAbUSvZYS2ppLR3BFWSkV6SsTJUx8BuDaj3P8vttkrog==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.798.0.tgz", + "integrity": "sha512-iYhNmHXfWLUwcMP9ldb/H+RMRLHZbBUWBgsoQqfb7sl6z24nH0qBJyL+oXHTCVBUYLP20CvUrVkcwlejDzyoRw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/nested-clients": "3.787.0", + "@aws-sdk/nested-clients": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", @@ -697,12 +701,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.787.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.787.0.tgz", - "integrity": "sha512-mG7Lz8ydfG4SF9e8WSXiPQ/Lsn3n8A5B5jtPROidafi06I3ckV2WxyMLdwG14m919NoS6IOfWHyRGSqWIwbVKA==", + "version": "3.798.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.798.0.tgz", + "integrity": "sha512-yncgNd2inI+y5kdfn2i0oBwgCxwdtcVShNNVQ+5b/nuC1Lgjgcb+hmHAeTFMge7vhDP2Md8I+ih6bPMpK79lQQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.787.0", + "@aws-sdk/middleware-user-agent": "3.798.0", "@aws-sdk/types": "3.775.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/types": "^4.2.0", @@ -721,24 +725,24 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", "dev": true, "license": "MIT", "engines": { @@ -746,22 +750,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -787,14 +791,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -804,14 +808,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", - "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -848,29 +852,29 @@ "license": "ISC" }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -880,9 +884,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { @@ -890,9 +894,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -900,9 +904,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -910,9 +914,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -920,27 +924,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", - "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.0" + "@babel/types": "^7.27.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -1047,13 +1051,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1173,13 +1177,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1189,45 +1193,42 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", "dev": true, "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", - "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.27.0", - "@babel/parser": "^7.27.0", - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1246,14 +1247,14 @@ } }, "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1315,9 +1316,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", "cpu": [ "ppc64" ], @@ -1332,9 +1333,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", "cpu": [ "arm" ], @@ -1349,9 +1350,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", "cpu": [ "arm64" ], @@ -1366,9 +1367,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", "cpu": [ "x64" ], @@ -1383,9 +1384,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", "cpu": [ "arm64" ], @@ -1400,9 +1401,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", "cpu": [ "x64" ], @@ -1417,9 +1418,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", "cpu": [ "arm64" ], @@ -1434,9 +1435,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", "cpu": [ "x64" ], @@ -1451,9 +1452,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", "cpu": [ "arm" ], @@ -1468,9 +1469,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", "cpu": [ "arm64" ], @@ -1485,9 +1486,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", "cpu": [ "ia32" ], @@ -1502,9 +1503,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", "cpu": [ "loong64" ], @@ -1519,9 +1520,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", "cpu": [ "mips64el" ], @@ -1536,9 +1537,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", "cpu": [ "ppc64" ], @@ -1553,9 +1554,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", "cpu": [ "riscv64" ], @@ -1570,9 +1571,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", "cpu": [ "s390x" ], @@ -1587,9 +1588,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", "cpu": [ "x64" ], @@ -1604,9 +1605,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", "cpu": [ "arm64" ], @@ -1621,9 +1622,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", "cpu": [ "x64" ], @@ -1638,9 +1639,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", "cpu": [ "arm64" ], @@ -1655,9 +1656,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", "cpu": [ "x64" ], @@ -1672,9 +1673,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", "cpu": [ "x64" ], @@ -1689,9 +1690,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", "cpu": [ "arm64" ], @@ -1706,9 +1707,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", "cpu": [ "ia32" ], @@ -1723,9 +1724,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", "cpu": [ "x64" ], @@ -1740,9 +1741,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", - "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -1929,13 +1930,16 @@ } }, "node_modules/@eslint/js": { - "version": "9.25.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz", - "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==", + "version": "9.28.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", + "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -1981,9 +1985,9 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", - "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz", + "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", "dev": true, "license": "MIT", "dependencies": { @@ -1991,20 +1995,20 @@ } }, "node_modules/@floating-ui/dom": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", - "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz", + "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", "dev": true, "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.6.0", + "@floating-ui/core": "^1.7.1", "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", - "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.3.tgz", + "integrity": "sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==", "dev": true, "license": "MIT", "dependencies": { @@ -2335,6 +2339,16 @@ } } }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.0.tgz", + "integrity": "sha512-xMbtoCeKJDto86GW6AiwVv7M4QAuI56R7dVBr1RNGYbOT44M2TIzOiske2RxopBqkumDY+A1H55pGvuribRY9A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -2396,734 +2410,1491 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/get-type": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.0.tgz", + "integrity": "sha512-VZWMjrBzqfDKngQ7sUctKeLxanAbsBFoZnPxNIG6CmxK7Gv6K44yqd0nzveNIBfuhGZMmk1n5PGbvdSTOu0yTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.0.tgz", + "integrity": "sha512-OEzYes5A1xwBJVMPqFRa8NCao8Vr42nsUZuf/SpaJWoLE+4kyl6nCQZ1zqfipmCrIXQVALC5qJwKy/7NQQLPhw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" + "@jest/environment": "30.0.0", + "@jest/expect": "30.0.0", + "@jest/types": "30.0.0", + "jest-mock": "30.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "node_modules/@jest/globals/node_modules/@jest/environment": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.0.tgz", + "integrity": "sha512-09sFbMMgS5JxYnvgmmtwIHhvoyzvR5fUPrVl8nOCrC5KdzmmErTcAxfWyAhJ2bv3rvHNQaKiS+COSG+O7oNbXw==", "dev": true, "license": "MIT", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", + "@jest/fake-timers": "30.0.0", + "@jest/types": "30.0.0", "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" + "jest-mock": "30.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "node_modules/@jest/globals/node_modules/@jest/expect": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.0.tgz", + "integrity": "sha512-XZ3j6syhMeKiBknmmc8V3mNIb44kxLTbOQtaXA4IFdHy+vEN0cnXRzbRjdGBtrp4k1PWyMWNU3Fjz3iejrhpQg==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.27.8" + "expect": "30.0.0", + "jest-snapshot": "30.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "node_modules/@jest/globals/node_modules/@jest/expect-utils": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.0.tgz", + "integrity": "sha512-UiWfsqNi/+d7xepfOv8KDcbbzcYtkWBe3a3kVDtg6M1kuN6CJ7b4HzIp5e1YHrSaQaVS8sdCoyCMCZClTLNKFQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "@jest/get-type": "30.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "node_modules/@jest/globals/node_modules/@jest/fake-timers": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.0.tgz", + "integrity": "sha512-yzBmJcrMHAMcAEbV2w1kbxmx8WFpEz8Cth3wjLMSkq+LO8VeGKRhpr5+BUp7PPK+x4njq/b6mVnDR8e/tPL5ng==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@jest/types": "30.0.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.0.0", + "jest-mock": "30.0.0", + "jest-util": "30.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "node_modules/@jest/globals/node_modules/@jest/schemas": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.0.tgz", + "integrity": "sha512-NID2VRyaEkevCRz6badhfqYwri/RvMbiHY81rk3AkK/LaiB0LSxi1RdVZ7MpZdTjNugtZeGfpL0mLs9Kp3MrQw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" + "@sinclair/typebox": "^0.34.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "node_modules/@jest/globals/node_modules/@jest/transform": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.0.tgz", + "integrity": "sha512-8xhpsCGYJsUjqpJOgLyMkeOSSlhqggFZEWAnZquBsvATtueoEs7CkMRxOUmJliF3E5x+mXmZ7gEEsHank029Og==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", + "@babel/core": "^7.27.4", + "@jest/types": "30.0.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.0", + "chalk": "^4.1.2", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.0", + "jest-regex-util": "30.0.0", + "jest-util": "30.0.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" + "write-file-atomic": "^5.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "node_modules/@jest/globals/node_modules/@jest/types": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.0.tgz", + "integrity": "sha512-1Nox8mAL52PKPfEnUQWBvKU/bp8FTT6AiDu76bFDEJj/qsRFSAVSldfCH3XYMqialti2zHXKvD5gN0AaHc0yKA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", + "@jest/pattern": "30.0.0", + "@jest/schemas": "30.0.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "node_modules/@jest/globals/node_modules/@sinclair/typebox": { + "version": "0.34.35", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.35.tgz", + "integrity": "sha512-C6ypdODf2VZkgRT6sFM8E1F8vR+HcffniX0Kp8MsU8PIfrlXbNCBz0jzj17GjdmjTx1OtZzdH8+iALL21UjF5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/globals/node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@jest/globals/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/globals/node_modules/babel-plugin-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", + "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@jest/globals/node_modules/ci-info": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", + "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "license": "MIT", "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "node_modules/@jest/globals/node_modules/expect": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.0.tgz", + "integrity": "sha512-xCdPp6gwiR9q9lsPCHANarIkFTN/IMZso6Kkq03sOm9IIGtzK/UJqml0dkhHibGh8HKOj8BIDIpZ0BZuU7QK6w==", "dev": true, "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.0.0", + "@jest/get-type": "30.0.0", + "jest-matcher-utils": "30.0.0", + "jest-message-util": "30.0.0", + "jest-mock": "30.0.0", + "jest-util": "30.0.0" + }, "engines": { - "node": ">=6.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "node_modules/@jest/globals/node_modules/jest-diff": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.0.tgz", + "integrity": "sha512-TgT1+KipV8JTLXXeFX0qSvIJR/UXiNNojjxb/awh3vYlBZyChU/NEmyKmq+wijKjWEztyrGJFL790nqMqNjTHA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@jest/diff-sequences": "30.0.0", + "@jest/get-type": "30.0.0", + "chalk": "^4.1.2", + "pretty-format": "30.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jsep-plugin/assignment": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", - "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "node_modules/@jest/globals/node_modules/jest-haste-map": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.0.tgz", + "integrity": "sha512-p4bXAhXTawTsADgQgTpbymdLaTyPW1xWNu1oIGG7/N3LIAbZVkH2JMJqS8/IUcnGR8Kc7WFE+vWbJvsqGCWZXw==", "dev": true, "license": "MIT", + "dependencies": { + "@jest/types": "30.0.0", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.0", + "jest-util": "30.0.0", + "jest-worker": "30.0.0", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, "engines": { - "node": ">= 10.16.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "peerDependencies": { - "jsep": "^0.4.0||^1.0.0" + "optionalDependencies": { + "fsevents": "^2.3.3" } }, - "node_modules/@jsep-plugin/regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", - "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "node_modules/@jest/globals/node_modules/jest-matcher-utils": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.0.tgz", + "integrity": "sha512-m5mrunqopkrqwG1mMdJxe1J4uGmS9AHHKYUmoxeQOxBcLjEvirIrIDwuKmUYrecPHVB/PUBpXs2gPoeA2FSSLQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 10.16.0" + "dependencies": { + "@jest/get-type": "30.0.0", + "chalk": "^4.1.2", + "jest-diff": "30.0.0", + "pretty-format": "30.0.0" }, - "peerDependencies": { - "jsep": "^0.4.0||^1.0.0" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/inspector": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector/-/inspector-0.8.2.tgz", - "integrity": "sha512-ewowTh84QVUrVnIVJLx5Jhh2yJrgWa/LrcVlNhtm8Y2Uk2bgW0y0catXyeyR6dqqyDadMXq1hbPRq0Lo1zPFnQ==", + "node_modules/@jest/globals/node_modules/jest-message-util": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.0.tgz", + "integrity": "sha512-pV3qcrb4utEsa/U7UI2VayNzSDQcmCllBZLSoIucrESRu0geKThFZOjjh0kACDJFJRAQwsK7GVsmS6SpEceD8w==", "dev": true, "license": "MIT", - "workspaces": [ - "client", - "server" - ], "dependencies": { - "@modelcontextprotocol/inspector-client": "^0.8.2", - "@modelcontextprotocol/inspector-server": "^0.8.2", - "concurrently": "^9.0.1", - "shell-quote": "^1.8.2", - "spawn-rx": "^5.1.2", - "ts-node": "^10.9.2" + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.0.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" }, - "bin": { - "mcp-inspector": "bin/cli.js" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/inspector-client": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-client/-/inspector-client-0.8.2.tgz", - "integrity": "sha512-eqsrA4eOXBadVTU8qFr2IYnxyY97+DfMDhl9LYuEHMIuEWjRObNGI/H3JFwDFpCkV4GOgWSBjfdIQ5yzUy7LKA==", + "node_modules/@jest/globals/node_modules/jest-mock": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.0.tgz", + "integrity": "sha512-W2sRA4ALXILrEetEOh2ooZG6fZ01iwVs0OWMKSSWRcUlaLr4ESHuiKXDNTg+ZVgOq8Ei5445i/Yxrv59VT+XkA==", "dev": true, "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.9.0", - "@radix-ui/react-checkbox": "^1.1.4", - "@radix-ui/react-dialog": "^1.1.3", - "@radix-ui/react-icons": "^1.3.0", - "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-popover": "^1.1.3", - "@radix-ui/react-select": "^2.1.2", - "@radix-ui/react-slot": "^1.1.0", - "@radix-ui/react-tabs": "^1.1.1", - "@radix-ui/react-toast": "^1.2.6", - "@radix-ui/react-tooltip": "^1.1.8", - "@types/prismjs": "^1.26.5", - "class-variance-authority": "^0.7.0", - "clsx": "^2.1.1", - "cmdk": "^1.0.4", - "lucide-react": "^0.447.0", - "pkce-challenge": "^4.1.0", - "prismjs": "^1.30.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-simple-code-editor": "^0.14.1", - "serve-handler": "^6.1.6", - "tailwind-merge": "^2.5.3", - "tailwindcss-animate": "^1.0.7", - "zod": "^3.23.8" + "@jest/types": "30.0.0", + "@types/node": "*", + "jest-util": "30.0.0" }, - "bin": { - "mcp-inspector-client": "bin/cli.js" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/inspector-server": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-server/-/inspector-server-0.8.2.tgz", - "integrity": "sha512-1gSgWAO20nT8YSdNi/3b4czELYWoc+Sur5+v2tX9ru1UIXQ10y3YMGDtmxm1hd3U5SXkuzeg6toXQyGTzcvvAQ==", + "node_modules/@jest/globals/node_modules/jest-regex-util": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.0.tgz", + "integrity": "sha512-rT84010qRu/5OOU7a9TeidC2Tp3Qgt9Sty4pOZ/VSDuEmRupIjKZAb53gU3jr4ooMlhwScrgC9UixJxWzVu9oQ==", "dev": true, "license": "MIT", - "dependencies": { - "@modelcontextprotocol/sdk": "^1.9.0", - "cors": "^2.8.5", - "express": "^4.21.0", - "ws": "^8.18.0", - "zod": "^3.23.8" - }, - "bin": { - "mcp-inspector-server": "build/index.js" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.10.2.tgz", - "integrity": "sha512-rb6AMp2DR4SN+kc6L1ta2NCpApyA9WYNx3CrTSZvGxq9wH71bRur+zRqPfg0vQ9mjywR7qZdX2RGHOPq3ss+tA==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.3", - "eventsource": "^3.0.2", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" - }, "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "node_modules/@jest/globals/node_modules/jest-snapshot": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.0.tgz", + "integrity": "sha512-6oCnzjpvfj/UIOMTqKZ6gedWAUgaycMdV8Y8h2dRJPvc2wSjckN03pzeoonw8y33uVngfx7WMo1ygdRGEKOT7w==", + "dev": true, "license": "MIT", "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.0.0", + "@jest/get-type": "30.0.0", + "@jest/snapshot-utils": "30.0.0", + "@jest/transform": "30.0.0", + "@jest/types": "30.0.0", + "babel-preset-current-node-syntax": "^1.1.0", + "chalk": "^4.1.2", + "expect": "30.0.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.0.0", + "jest-matcher-utils": "30.0.0", + "jest-message-util": "30.0.0", + "jest-util": "30.0.0", + "pretty-format": "30.0.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" }, "engines": { - "node": ">= 0.6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "node_modules/@jest/globals/node_modules/jest-util": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.0.tgz", + "integrity": "sha512-fhNBBM9uSUbd4Lzsf8l/kcAdaHD/4SgoI48en3HXcBEMwKwoleKFMZ6cYEYs21SB779PRuRCyNLmymApAm8tZw==", + "dev": true, "license": "MIT", "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "@jest/types": "30.0.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" }, "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "node_modules/@jest/globals/node_modules/jest-worker": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.0.tgz", + "integrity": "sha512-VZvxfWIybIvwK8N/Bsfe43LfQgd/rD0c4h5nLUx78CAqPxIQcW2qDjsVAC53iUR8yxzFIeCFFvWOh8en8hGzdg==", + "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "5.2.1" + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.0.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" }, "engines": { - "node": ">= 0.6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "node_modules/@jest/globals/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=6.6.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "node_modules/@jest/globals/node_modules/pretty-format": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.0.tgz", + "integrity": "sha512-18NAOUr4ZOQiIR+BgI5NhQE7uREdx4ZyV0dyay5izh4yfQ+1T7BSvggxvRGoXocrRyevqW5OhScUjbi9GB8R8Q==", + "dev": true, "license": "MIT", "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" + "@jest/schemas": "30.0.0", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" }, "engines": { - "node": ">= 18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals/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": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "node_modules/@jest/globals/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", + "node_modules/@jest/globals/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, "engines": { - "node": ">= 0.8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/@jest/pattern": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.0.tgz", + "integrity": "sha512-k+TpEThzLVXMkbdxf8KHjZ83Wl+G54ytVJoDIGWwS96Ql4xyASRjc6SU1hs5jHVql+hpyK9G8N7WuFhLpGHRpQ==", + "dev": true, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "@types/node": "*", + "jest-regex-util": "30.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "node_modules/@jest/pattern/node_modules/jest-regex-util": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.0.tgz", + "integrity": "sha512-rT84010qRu/5OOU7a9TeidC2Tp3Qgt9Sty4pOZ/VSDuEmRupIjKZAb53gU3jr4ooMlhwScrgC9UixJxWzVu9oQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, "engines": { - "node": ">=18" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, "engines": { - "node": ">= 0.6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "node_modules/@jest/snapshot-utils": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.0.tgz", + "integrity": "sha512-C/QSFUmvZEYptg2Vin84FggAphwHvj6la39vkw1CNOZQORWZ7O/H0BXmdeeeGnvlXDYY8TlFM5jgFnxLAxpFjA==", + "dev": true, "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" + "@jest/types": "30.0.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" }, "engines": { - "node": ">= 0.6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "node_modules/@jest/snapshot-utils/node_modules/@jest/schemas": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.0.tgz", + "integrity": "sha512-NID2VRyaEkevCRz6badhfqYwri/RvMbiHY81rk3AkK/LaiB0LSxi1RdVZ7MpZdTjNugtZeGfpL0mLs9Kp3MrQw==", + "dev": true, "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, "engines": { - "node": ">= 0.6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/pkce-challenge": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", - "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "node_modules/@jest/snapshot-utils/node_modules/@jest/types": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.0.tgz", + "integrity": "sha512-1Nox8mAL52PKPfEnUQWBvKU/bp8FTT6AiDu76bFDEJj/qsRFSAVSldfCH3XYMqialti2zHXKvD5gN0AaHc0yKA==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.0", + "@jest/schemas": "30.0.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, "engines": { - "node": ">=16.20.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", + "node_modules/@jest/snapshot-utils/node_modules/@sinclair/typebox": { + "version": "0.34.35", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.35.tgz", + "integrity": "sha512-C6ypdODf2VZkgRT6sFM8E1F8vR+HcffniX0Kp8MsU8PIfrlXbNCBz0jzj17GjdmjTx1OtZzdH8+iALL21UjF5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", "dependencies": { - "side-channel": "^1.1.0" + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" }, "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, "engines": { - "node": ">= 18" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, "license": "MIT", "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": ">= 18" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, "license": "MIT", "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" }, "engines": { - "node": ">= 0.6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mongodb-js/devtools-connect": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-connect/-/devtools-connect-3.7.2.tgz", - "integrity": "sha512-fT5QPn/hR9xl5yfFUMcBbI8smidq3JHZDlV4//srqZVxqtor2ofHdxua1kDnQEpv8sclTY/5o6TjoYQ8IiNaIQ==", - "license": "Apache-2.0", + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", "dependencies": { - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/oidc-http-server-pages": "1.1.4", - "lodash.merge": "^4.6.2", - "mongodb-connection-string-url": "^3.0.0", - "socks": "^2.7.3" - }, - "optionalDependencies": { - "kerberos": "^2.1.0", - "mongodb-client-encryption": "^6.1.0", - "os-dns-native": "^1.2.0", - "resolve-mongodb-srv": "^1.1.1" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, - "peerDependencies": { - "@mongodb-js/oidc-plugin": "^1.1.0", - "mongodb": "^6.9.0", - "mongodb-log-writer": "^2.4.1" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mongodb-js/devtools-proxy-support": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-proxy-support/-/devtools-proxy-support-0.4.4.tgz", - "integrity": "sha512-klRFd33bjUntPJuEY86NB0xYd64SaEYN0ABbE5fjU8+lO94ItvxTAWyHUmerPFAk8OLyz1MFyDoTXOvdOs9NAQ==", - "license": "Apache-2.0", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", "dependencies": { - "@mongodb-js/socksv5": "^0.0.10", - "agent-base": "^7.1.1", - "debug": "^4.4.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", - "lru-cache": "^11.0.0", - "node-fetch": "^3.3.2", - "pac-proxy-agent": "^7.0.2", - "socks-proxy-agent": "^8.0.4", - "ssh2": "^1.15.0", - "system-ca": "^2.0.1" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@mongodb-js/mongodb-downloader": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-downloader/-/mongodb-downloader-0.3.9.tgz", - "integrity": "sha512-6lEIESINiIAeQUw95+hkfxG6129r6KiPU2TNOcxb30PsGgFHPJFg7QY8UoSQXjDE9YaENlr6oQm3c1XDixWeEg==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.4.0", - "decompress": "^4.2.1", - "mongodb-download-url": "^1.5.7", - "node-fetch": "^2.7.0", - "tar": "^6.1.15" + "license": "MIT", + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@mongodb-js/mongodb-downloader/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", "dependencies": { - "whatwg-url": "^5.0.0" - }, + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "dev": true, + "license": "MIT", "engines": { - "node": "4.x || >=6.0.0" + "node": ">= 10.16.0" }, "peerDependencies": { - "encoding": "^0.1.0" + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.16.0" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" } }, - "node_modules/@mongodb-js/mongodb-downloader/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "node_modules/@modelcontextprotocol/inspector": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector/-/inspector-0.14.1.tgz", + "integrity": "sha512-F9Xd/06eCfeuHkFR+6sNGhGrKAfgXFmf9VDzw+hMMoeJM3ltOUSJh/fqCvvM6tGrxvb49uAF7dr/o5Dz+rVKxw==", "dev": true, - "license": "MIT" + "license": "MIT", + "workspaces": [ + "client", + "server", + "cli" + ], + "dependencies": { + "@modelcontextprotocol/inspector-cli": "^0.14.1", + "@modelcontextprotocol/inspector-client": "^0.14.1", + "@modelcontextprotocol/inspector-server": "^0.14.1", + "@modelcontextprotocol/sdk": "^1.12.1", + "concurrently": "^9.0.1", + "open": "^10.1.0", + "shell-quote": "^1.8.2", + "spawn-rx": "^5.1.2", + "ts-node": "^10.9.2", + "zod": "^3.23.8" + }, + "bin": { + "mcp-inspector": "cli/build/cli.js" + } + }, + "node_modules/@modelcontextprotocol/inspector-cli": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-cli/-/inspector-cli-0.14.1.tgz", + "integrity": "sha512-PDXugYewCHnhfEmoMLczJ/rtniCJN9evFkJZVq9o1CVBHme578V3MIT7uk4ap/M6xM26+9OAixdbYp9Rf7V8VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "commander": "^13.1.0", + "spawn-rx": "^5.1.2" + }, + "bin": { + "mcp-inspector-cli": "build/cli.js" + } + }, + "node_modules/@modelcontextprotocol/inspector-client": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-client/-/inspector-client-0.14.1.tgz", + "integrity": "sha512-nJym4J7R3xChNSDYBdfRyI/r4eS0uUl85/GQxIi4STnNJI6ajv6sicCWx5uRL74Ed5GFME9SS/xI3Tdm+aqtrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.3", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-popover": "^1.1.3", + "@radix-ui/react-select": "^2.1.2", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.1", + "@radix-ui/react-toast": "^1.2.6", + "@radix-ui/react-tooltip": "^1.1.8", + "ajv": "^6.12.6", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.4", + "lucide-react": "^0.447.0", + "pkce-challenge": "^4.1.0", + "prismjs": "^1.30.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-simple-code-editor": "^0.14.1", + "serve-handler": "^6.1.6", + "tailwind-merge": "^2.5.3", + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.8" + }, + "bin": { + "mcp-inspector-client": "bin/start.js" + } + }, + "node_modules/@modelcontextprotocol/inspector-client/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/inspector-client/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/inspector-server": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-server/-/inspector-server-0.14.1.tgz", + "integrity": "sha512-w9VTdqzHHYBOOQw0QJa2QB/tn6dTZsDSGO3d4a5BJile4It283Lw1xi1W1pMgNB+MTEG471disU/qJapqETK6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "cors": "^2.8.5", + "express": "^5.1.0", + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "bin": { + "mcp-inspector-server": "build/index.js" + } + }, + "node_modules/@modelcontextprotocol/inspector/node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/inspector/node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/inspector/node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/inspector/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/inspector/node_modules/open": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", + "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/inspector/node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.1.tgz", + "integrity": "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@mongodb-js/device-id": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/device-id/-/device-id-0.2.1.tgz", + "integrity": "sha512-kC/F1/ryJMNeIt+n7CATAf9AL/X5Nz1Tju8VseyViL2DF640dmF/JQwWmjakpsSTy5X9TVNOkG9ye4Mber8GHQ==", + "license": "Apache-2.0" + }, + "node_modules/@mongodb-js/devtools-connect": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-connect/-/devtools-connect-3.7.2.tgz", + "integrity": "sha512-fT5QPn/hR9xl5yfFUMcBbI8smidq3JHZDlV4//srqZVxqtor2ofHdxua1kDnQEpv8sclTY/5o6TjoYQ8IiNaIQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/devtools-proxy-support": "^0.4.4", + "@mongodb-js/oidc-http-server-pages": "1.1.4", + "lodash.merge": "^4.6.2", + "mongodb-connection-string-url": "^3.0.0", + "socks": "^2.7.3" + }, + "optionalDependencies": { + "kerberos": "^2.1.0", + "mongodb-client-encryption": "^6.1.0", + "os-dns-native": "^1.2.0", + "resolve-mongodb-srv": "^1.1.1" + }, + "peerDependencies": { + "@mongodb-js/oidc-plugin": "^1.1.0", + "mongodb": "^6.9.0", + "mongodb-log-writer": "^2.4.1" + } + }, + "node_modules/@mongodb-js/devtools-proxy-support": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-proxy-support/-/devtools-proxy-support-0.4.4.tgz", + "integrity": "sha512-klRFd33bjUntPJuEY86NB0xYd64SaEYN0ABbE5fjU8+lO94ItvxTAWyHUmerPFAk8OLyz1MFyDoTXOvdOs9NAQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/socksv5": "^0.0.10", + "agent-base": "^7.1.1", + "debug": "^4.4.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "lru-cache": "^11.0.0", + "node-fetch": "^3.3.2", + "pac-proxy-agent": "^7.0.2", + "socks-proxy-agent": "^8.0.4", + "ssh2": "^1.15.0", + "system-ca": "^2.0.1" + } + }, + "node_modules/@mongodb-js/mongodb-downloader": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-downloader/-/mongodb-downloader-0.4.0.tgz", + "integrity": "sha512-+xBDhZQn+tGY8bWZo9gE+Nd6Mw2r6TQWaLbgGw/H50M5ky092IS4vbTjwvJhR2m/efoxj31LZpZI0PYK7Pzu6w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.0", + "decompress": "^4.2.1", + "mongodb-download-url": "^1.6.0", + "node-fetch": "^2.7.0", + "tar": "^6.1.15" + } + }, + "node_modules/@mongodb-js/mongodb-downloader/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@mongodb-js/mongodb-downloader/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mongodb-js/mongodb-downloader/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/@mongodb-js/mongodb-downloader/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/@mongodb-js/oidc-http-server-pages": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-http-server-pages/-/oidc-http-server-pages-1.1.4.tgz", + "integrity": "sha512-fPwS1cERLGNSz8D1kBw2RJ0GNn1Ud2IIBehvV8OmOZzSXEx6hjwgvKG8XdHT7tpXns7iSkw9gSj84yHJkAlOnQ==", + "license": "Apache-2.0" + }, + "node_modules/@mongodb-js/oidc-plugin": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-plugin/-/oidc-plugin-1.1.7.tgz", + "integrity": "sha512-+90E2JFrJuMk1dlT/LlZ4yaJj0Xtc6Qcf7FXphgu2j+EElWY/8y/GalFqf/KC/Wd1qt8EuR8Jnr6Pq+Q+ptASg==", + "license": "Apache-2.0", + "dependencies": { + "express": "^4.18.2", + "open": "^9.1.0", + "openid-client": "^5.6.4" + }, + "engines": { + "node": ">= 16.20.1" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } }, - "node_modules/@mongodb-js/mongodb-downloader/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" + "node_modules/@mongodb-js/oidc-plugin/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/@mongodb-js/mongodb-downloader/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, + "node_modules/@mongodb-js/oidc-plugin/node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "license": "MIT", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/@mongodb-js/oidc-http-server-pages": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-http-server-pages/-/oidc-http-server-pages-1.1.4.tgz", - "integrity": "sha512-fPwS1cERLGNSz8D1kBw2RJ0GNn1Ud2IIBehvV8OmOZzSXEx6hjwgvKG8XdHT7tpXns7iSkw9gSj84yHJkAlOnQ==", - "license": "Apache-2.0" - }, - "node_modules/@mongodb-js/oidc-plugin": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-plugin/-/oidc-plugin-1.1.6.tgz", - "integrity": "sha512-fuL4B9x1njcqdJqV+V3pt8s/9PX4uy9ojhcsP12BavDcg61ju6WEqCkDmUZCykDIvsDbb8tIhO97aCKDxcXROw==", - "license": "Apache-2.0", + "node_modules/@mongodb-js/oidc-plugin/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { - "express": "^4.18.2", - "open": "^9.1.0", - "openid-client": "^5.6.4" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" }, "engines": { - "node": ">= 16.20.1" + "node": ">= 0.6" } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz", - "integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -3151,15 +3922,15 @@ } }, "node_modules/@mongosh/service-provider-core": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@mongosh/service-provider-core/-/service-provider-core-3.3.0.tgz", - "integrity": "sha512-gzVO33L4hqZqw3dKuJyXXQbTJsibW6k4U01WeaV+H2OzIyqaNPxdMHK+slrM7rizYM+5UG+F0YNYvFDrenjAIw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@mongosh/service-provider-core/-/service-provider-core-3.3.3.tgz", + "integrity": "sha512-Cylm0JjY0iu2C91o3koGNDtx7WhhFhCo+zWSxD5+aFiuAxrQQEmVxqLGFB9QTHwUotsdk2i7zi2lMdYVtCnkCA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-providers": "^3.525.0", "@mongosh/errors": "2.4.0", "bson": "^6.10.3", - "mongodb": "^6.14.2", + "mongodb": "^6.16.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1" }, @@ -3171,18 +3942,18 @@ } }, "node_modules/@mongosh/service-provider-node-driver": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@mongosh/service-provider-node-driver/-/service-provider-node-driver-3.8.0.tgz", - "integrity": "sha512-r+SfWIT6HlJsYuDJcHJX6upcifEisqKtafEmjXkbw69ObnrHfyj0PRFa+ymeE3xFRiGSLpw7rI/39bz2g6rsBA==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/@mongosh/service-provider-node-driver/-/service-provider-node-driver-3.8.3.tgz", + "integrity": "sha512-/AN1tCy7T/wUA88M2CiUuUAZg6UxkzfJlfk3OvpN7EVezl+P80xSv2MW+MsHX9o3Qa8g6oDHog26bRqM7YehJQ==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/devtools-connect": "^3.4.1", - "@mongodb-js/oidc-plugin": "^1.1.6", + "@mongodb-js/oidc-plugin": "^1.1.7", "@mongosh/errors": "2.4.0", - "@mongosh/service-provider-core": "3.3.0", - "@mongosh/types": "3.6.0", + "@mongosh/service-provider-core": "3.3.3", + "@mongosh/types": "3.6.2", "aws4": "^1.12.0", - "mongodb": "^6.14.2", + "mongodb": "^6.16.0", "mongodb-connection-string-url": "^3.0.1", "socks": "^2.8.3" }, @@ -3245,9 +4016,9 @@ } }, "node_modules/@mongosh/types": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@mongosh/types/-/types-3.6.0.tgz", - "integrity": "sha512-p6NXCTa4FjjTQAQJk9OehfXKFIE/vdQJOqcMbVR3Cxg2zVCnfV16NDnuxpFHYnLkgqL9Cz10BtUGSZPDMFJXew==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@mongosh/types/-/types-3.6.2.tgz", + "integrity": "sha512-3qqXkdwQYVB+/u7AR1nqlUxY8QaM7O2m15/CH55n7iAlIlAgwtuSjB+DLXOBNxh4AcCPcakyilWIlZr6pCpkgA==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/devtools-connect": "^3.4.1" @@ -3538,6 +4309,19 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", + "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -3627,13 +4411,13 @@ "license": "MIT" }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.4.tgz", - "integrity": "sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -3651,17 +4435,17 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.2.2.tgz", - "integrity": "sha512-pMxzQLK+m/tkDRXJg7VUjRx6ozsBdzNLOV4vexfVBU57qT2Gvf4cw2gKKhOohJxjadQ+WcUXCKosTIxcZzi03A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz", + "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" @@ -3682,16 +4466,16 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.4.tgz", - "integrity": "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -3741,23 +4525,23 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.10.tgz", - "integrity": "sha512-m6pZb0gEM5uHPSb+i2nKKGQi/HMSVjARMsLMWQfKDP+eJ6B+uqryHnXhpnohTWElw+vEcMk/o4wJODtdRKHwqg==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.6", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" @@ -3794,15 +4578,15 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.7.tgz", - "integrity": "sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, @@ -3838,14 +4622,14 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.4.tgz", - "integrity": "sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { @@ -3893,13 +4677,13 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.4.tgz", - "integrity": "sha512-wy3dqizZnZVV4ja0FNnUhIWNwWdoldXrneEyUcVtLYDAt8ovGS4ridtMAOGgXBBIfggL4BOveVWsjXDORdGEQg==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -3917,24 +4701,24 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.10.tgz", - "integrity": "sha512-IZN7b3sXqajiPsOzKuNJBSP9obF4MX5/5UhTgWNofw4r1H+eATWb0SyMlaxPD/kzA4vadFgy1s7Z1AEJ6WMyHQ==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", + "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" @@ -3955,17 +4739,17 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.4.tgz", - "integrity": "sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", "dev": true, "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.4", + "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", @@ -3988,13 +4772,13 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.6.tgz", - "integrity": "sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { @@ -4013,9 +4797,9 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz", - "integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", "dev": true, "license": "MIT", "dependencies": { @@ -4038,13 +4822,13 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", - "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.0" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -4062,19 +4846,19 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.7.tgz", - "integrity": "sha512-C6oAg451/fQT3EGbWHbCQjYTtbyjNO1uzQgMzwyivcHT3GKNEmu1q3UuREhN+HzHAVtv3ivMVK08QlC+PkYw9Q==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, @@ -4094,31 +4878,31 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.2.tgz", - "integrity": "sha512-HjkVHtBkuq+r3zUAZ/CvNWUGKPfuicGDbgtZgiQuFmNcV5F+Tgy24ep2nsAW2nFgvhGPJVqeBZa6KyVN0EyrBA==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz", + "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.0", + "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, @@ -4138,9 +4922,9 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "dev": true, "license": "MIT", "dependencies": { @@ -4157,9 +4941,9 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.8.tgz", - "integrity": "sha512-4iUaN9SYtG+/E+hJ7jRks/Nv90f+uAsRHbLYA6BcA9EsR6GNWgsvtS4iwU2SP0tOZfDGAyqIT0yz7ckgohEIFA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz", + "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", "dev": true, "license": "MIT", "dependencies": { @@ -4167,9 +4951,9 @@ "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-roving-focus": "1.1.7", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -4188,24 +4972,24 @@ } }, "node_modules/@radix-ui/react-toast": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.10.tgz", - "integrity": "sha512-lVe1mQL8Di8KPQp62CDaLgttqyUGTchPuwDiCnaZz40HGxngJKB/fOJCHYxHZh2p1BtcuiPOYOKrxTVEmrnV5A==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.14.tgz", + "integrity": "sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", - "@radix-ui/react-portal": "1.1.6", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.0" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -4223,24 +5007,24 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.3.tgz", - "integrity": "sha512-0KX7jUYFA02np01Y11NWkk6Ip6TqMNmD4ijLelYAzeIndl2aVeltjJFJ2gwjNa1P8U/dgjQ+8cr9Y3Ni+ZNoRA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz", + "integrity": "sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.0" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -4402,13 +5186,13 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.0.tgz", - "integrity": "sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -4450,9 +5234,9 @@ } }, "node_modules/@redocly/cli": { - "version": "1.34.2", - "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-1.34.2.tgz", - "integrity": "sha512-XnKG7yrr0GUZ7MyqHk9hRt//HGUSnLDwwEXI8HDQdyDFp+YFbddqcQPOWc/sDeDBTEiBXqwPOb3/VKA+8NtSMw==", + "version": "1.34.3", + "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-1.34.3.tgz", + "integrity": "sha512-GJNBTMfm5wTCtH6K+RtPQZuGbqflMclXqAZ5My12tfux6xFDMW1l0MNd5RMpnIS1aeFcDX++P1gnnROWlesj4w==", "dev": true, "license": "MIT", "dependencies": { @@ -4462,8 +5246,8 @@ "@opentelemetry/sdk-trace-node": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", "@redocly/config": "^0.22.0", - "@redocly/openapi-core": "1.34.2", - "@redocly/respect-core": "1.34.2", + "@redocly/openapi-core": "1.34.3", + "@redocly/respect-core": "1.34.3", "abort-controller": "^3.0.0", "chokidar": "^3.5.1", "colorette": "^1.2.0", @@ -4477,7 +5261,7 @@ "pluralize": "^8.0.0", "react": "^17.0.0 || ^18.2.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.2.0 || ^19.0.0", - "redoc": "2.4.0", + "redoc": "2.5.0", "semver": "^7.5.2", "simple-websocket": "^9.0.0", "styled-components": "^6.0.7", @@ -4500,9 +5284,9 @@ "license": "MIT" }, "node_modules/@redocly/openapi-core": { - "version": "1.34.2", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.2.tgz", - "integrity": "sha512-glfkQFJizLdq2fBkNvc2FJW0sxDb5exd0wIXhFk+WHaFLMREBC3CxRo2Zq7uJIdfV9U3YTceMbXJklpDfmmwFQ==", + "version": "1.34.3", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.3.tgz", + "integrity": "sha512-3arRdUp1fNx55itnjKiUhO6t4Mf91TsrTIYINDNLAZPS0TPd5YpiXRctwjel0qqWoOOhjA34cZ3m4dksLDFUYg==", "dev": true, "license": "MIT", "dependencies": { @@ -4522,15 +5306,15 @@ } }, "node_modules/@redocly/respect-core": { - "version": "1.34.2", - "resolved": "https://registry.npmjs.org/@redocly/respect-core/-/respect-core-1.34.2.tgz", - "integrity": "sha512-X6VR9bbHXrI01Wh5t6TIHxFCVHcP4Iy42micKLIk/Cg6EmHVbaSDGOD6mxChXtEIrwnY+bqyUbjlXr9+YM7B9A==", + "version": "1.34.3", + "resolved": "https://registry.npmjs.org/@redocly/respect-core/-/respect-core-1.34.3.tgz", + "integrity": "sha512-vo/gu7dRGwTVsRueVSjVk04jOQuL0w22RBJRdRUWkfyse791tYXgMCOx35ijKekL83Q/7Okxf/YX6UY1v5CAug==", "dev": true, "license": "MIT", "dependencies": { "@faker-js/faker": "^7.6.0", "@redocly/ajv": "8.11.2", - "@redocly/openapi-core": "1.34.2", + "@redocly/openapi-core": "1.34.3", "better-ajv-errors": "^1.2.0", "colorette": "^2.0.20", "concat-stream": "^2.0.0", @@ -4650,10 +5434,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@redocly/respect-core/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@redocly/respect-core/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@redocly/respect-core/node_modules/open": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.1.tgz", - "integrity": "sha512-zy1wx4+P3PfhXSEPJNtZmJXfhkkIaxU1VauWIrDZw1O7uJRDRJtKr9n3Ic4NgbA16KyOxOXO2ng9gYwCdXuSXA==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", + "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", "dev": true, "license": "MIT", "dependencies": { @@ -4766,9 +5573,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.2.0.tgz", - "integrity": "sha512-k17bgQhVZ7YmUvA8at4af1TDpl0NDMBuBKJl8Yg0nrefwmValU+CnA5l/AriVdQNthU/33H3nK71HrLgqOPr1Q==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.3.0.tgz", + "integrity": "sha512-r6gvs5OfRq/w+9unPm7B3po4rmWaGh0CIL/OwHntGGux7+RhOOZLGuurbeMgWV6W55ZuyMTypJLeH0vn/ZRaWQ==", "license": "Apache-2.0", "dependencies": { "@smithy/middleware-serde": "^4.0.3", @@ -4871,12 +5678,12 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.0.tgz", - "integrity": "sha512-xhLimgNCbCzsUppRTGXWkZywksuTThxaIB0HwbpsVLY5sceac4e1TZ/WKYqufQLaUy+gUSJGNdwD2jo3cXL0iA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.1.tgz", + "integrity": "sha512-z5RmcHxjvScL+LwEDU2mTNCOhgUs4lu5PGdF1K36IPRmUHhNFxNxgenSB7smyDiYD4vdKQ7CAZtG5cUErqib9w==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.2.0", + "@smithy/core": "^3.3.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/node-config-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", @@ -4890,15 +5697,15 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.0.tgz", - "integrity": "sha512-2zAagd1s6hAaI/ap6SXi5T3dDwBOczOMCSkkYzktqN1+tzbk1GAsHNAdo/1uzxz3Ky02jvZQwbi/vmDA6z4Oyg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.1.tgz", + "integrity": "sha512-mBJOxn9aUYwcBUPQpKv9ifzrCn4EbhPUFguEZv3jB57YOMh0caS4P8HoLvUeNUI1nx4bIVH2SIbogbDfFI9DUA==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/service-error-classification": "^4.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.1", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", @@ -5045,9 +5852,9 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.2.tgz", - "integrity": "sha512-Mz+mc7okA73Lyz8zQKJNyr7lIcHLiPYp0+oiqiMNc/t7/Kf2BENs5d63pEj7oPqdjaum6g0Fc8wC78dY1TgtXw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.0.tgz", + "integrity": "sha512-4t5WX60sL3zGJF/CtZsUQTs3UrZEDO2P7pEaElrekbLqkWPYkgqNW1oeiNYC6xXifBnT9dVBOnNQRvOE9riU9w==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.0.0", @@ -5064,13 +5871,13 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.0.tgz", - "integrity": "sha512-Qs65/w30pWV7LSFAez9DKy0Koaoh3iHhpcpCCJ4waj/iqwsuSzJna2+vYwq46yBaqO5ZbP9TjUsATUNxrKeBdw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.1.tgz", + "integrity": "sha512-fbniZef60QdsBc4ZY0iyI8xbFHIiC/QRtPi66iE4ufjiE/aaz7AfUXzcWMkpO8r+QhLeNRIfmPchIG+3/QDZ6g==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.2.0", - "@smithy/middleware-endpoint": "^4.1.0", + "@smithy/core": "^3.3.0", + "@smithy/middleware-endpoint": "^4.1.1", "@smithy/middleware-stack": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", @@ -5171,13 +5978,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.8.tgz", - "integrity": "sha512-ZTypzBra+lI/LfTYZeop9UjoJhhGRTg3pxrNpfSTQLd3AJ37r2z4AXTKpq1rFXiiUIJsYyFgNJdjWRGP/cbBaQ==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.9.tgz", + "integrity": "sha512-B8j0XsElvyhv6+5hlFf6vFV/uCSyLKcInpeXOGnOImX2mGXshE01RvPoGipTlRpIk53e6UfYj7WdDdgbVfXDZw==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.1", "@smithy/types": "^4.2.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -5187,16 +5994,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.8.tgz", - "integrity": "sha512-Rgk0Jc/UDfRTzVthye/k2dDsz5Xxs9LZaKCNPgJTRyoyBoeiNCnHsYGOyu1PKN+sDyPnJzMOz22JbwxzBp9NNA==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.9.tgz", + "integrity": "sha512-wTDU8P/zdIf9DOpV5qm64HVgGRXvqjqB/fJZTEQbrz3s79JHM/E7XkMm/876Oq+ZLHJQgnXM9QHDo29dlM62eA==", "license": "Apache-2.0", "dependencies": { "@smithy/config-resolver": "^4.1.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/property-provider": "^4.0.2", - "@smithy/smithy-client": "^4.2.0", + "@smithy/smithy-client": "^4.2.1", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, @@ -5443,22 +6250,15 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "version": "22.15.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz", + "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, - "node_modules/@types/prismjs": { - "version": "1.26.5", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", - "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/simple-oauth2": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/@types/simple-oauth2/-/simple-oauth2-5.0.7.tgz", @@ -5521,21 +6321,21 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz", - "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", + "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.31.0", - "@typescript-eslint/type-utils": "8.31.0", - "@typescript-eslint/utils": "8.31.0", - "@typescript-eslint/visitor-keys": "8.31.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/type-utils": "8.34.0", + "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5545,22 +6345,32 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "@typescript-eslint/parser": "^8.34.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz", - "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", + "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.31.0", - "@typescript-eslint/types": "8.31.0", - "@typescript-eslint/typescript-estree": "8.31.0", - "@typescript-eslint/visitor-keys": "8.31.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4" }, "engines": { @@ -5575,35 +6385,74 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", + "integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.34.0", + "@typescript-eslint/types": "^8.34.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz", - "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", + "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.31.0", - "@typescript-eslint/visitor-keys": "8.31.0" + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", + "integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", + "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz", - "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", + "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.31.0", - "@typescript-eslint/utils": "8.31.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/utils": "8.34.0", "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5618,9 +6467,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", - "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", + "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", "dev": true, "license": "MIT", "engines": { @@ -5632,20 +6481,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz", - "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", + "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.31.0", - "@typescript-eslint/visitor-keys": "8.31.0", + "@typescript-eslint/project-service": "8.34.0", + "@typescript-eslint/tsconfig-utils": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5675,16 +6526,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz", - "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", + "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.31.0", - "@typescript-eslint/types": "8.31.0", - "@typescript-eslint/typescript-estree": "8.31.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5699,13 +6550,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz", - "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", + "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/types": "8.34.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -5716,6 +6567,13 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -5730,13 +6588,13 @@ } }, "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" @@ -5886,9 +6744,9 @@ "license": "Python-2.0" }, "node_modules/aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "dev": true, "license": "MIT", "dependencies": { @@ -6163,8 +7021,8 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "devOptional": true, "license": "MIT", + "optional": true, "dependencies": { "file-uri-to-path": "1.0.0" } @@ -6214,57 +7072,23 @@ } }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/body-parser/node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=18" } }, "node_modules/bowser": { @@ -6309,9 +7133,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", "dev": true, "funding": [ { @@ -6329,10 +7153,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -6365,9 +7189,9 @@ } }, "node_modules/bson": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.3.tgz", - "integrity": "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", "license": "Apache-2.0", "engines": { "node": ">=16.20.1" @@ -6540,9 +7364,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001715", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz", - "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==", + "version": "1.0.30001723", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", + "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", "dev": true, "funding": [ { @@ -6792,11 +7616,14 @@ } }, "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/concat-map": { "version": "0.0.1", @@ -6897,10 +7724,20 @@ "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": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -6935,15 +7772,18 @@ } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } }, "node_modules/core-js": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.41.0.tgz", - "integrity": "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==", + "version": "3.42.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.42.0.tgz", + "integrity": "sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7624,9 +8464,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.140", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.140.tgz", - "integrity": "sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==", + "version": "1.5.168", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.168.tgz", + "integrity": "sha512-RUNQmFLNIWVW6+z32EJQ5+qx8ci6RGvdtDC0Ls+F89wz6I2AthpXF0w0DIrn2jpLX0/PU9ZCo+Qp7bg/EckJmA==", "dev": true, "license": "ISC" }, @@ -7733,9 +8573,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7746,31 +8586,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" } }, "node_modules/escalade": { @@ -7824,9 +8664,9 @@ } }, "node_modules/eslint": { - "version": "9.25.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", - "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", + "version": "9.26.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz", + "integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7836,11 +8676,12 @@ "@eslint/config-helpers": "^0.2.1", "@eslint/core": "^0.13.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.25.1", + "@eslint/js": "9.26.0", "@eslint/plugin-kit": "^0.2.8", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", + "@modelcontextprotocol/sdk": "^1.8.0", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", @@ -7864,7 +8705,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3" + "optionator": "^0.9.3", + "zod": "^3.24.2" }, "bin": { "eslint": "bin/eslint.js" @@ -7885,18 +8727,78 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz", - "integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==", + "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==", "dev": true, "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, "peerDependencies": { "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-jest": { + "version": "28.12.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.12.0.tgz", + "integrity": "sha512-J6zmDp8WiQ9tyvYXE+3RFy7/+l4hraWLzmsabYXyehkmmDd36qV4VQFc7XzcsD8C1PTNt646MSx25bO1mdd9Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "engines": { + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz", + "integrity": "sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", @@ -7927,6 +8829,16 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "9.26.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz", + "integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/eslint/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -8170,45 +9082,41 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 18" }, "funding": { "type": "opencollective", @@ -8230,37 +9138,19 @@ "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -8282,7 +9172,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -8419,8 +9308,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "devOptional": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/filelist": { "version": "1.0.4", @@ -8446,38 +9335,22 @@ } }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "license": "MIT", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { "node": ">= 0.8" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -8539,6 +9412,29 @@ "node": ">= 6" } }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -8561,12 +9457,12 @@ } }, "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/fs-constants": { @@ -8832,9 +9728,9 @@ } }, "node_modules/globals": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", - "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz", + "integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==", "dev": true, "license": "MIT", "engines": { @@ -9017,12 +9913,12 @@ } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -9623,6 +10519,16 @@ "node": ">=12" } }, + "node_modules/jest-cli/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/jest-config": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", @@ -9734,24 +10640,27 @@ } }, "node_modules/jest-extended": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-4.0.2.tgz", - "integrity": "sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-6.0.0.tgz", + "integrity": "sha512-SM249N/q33YQ9XE8E06qZSnFuuV4GQFx7WrrmIj4wQUAP43jAo6budLT482jdBhf8ASwUiEEfJNjej0UusYs5A==", "dev": true, "license": "MIT", "dependencies": { - "jest-diff": "^29.0.0", - "jest-get-type": "^29.0.0" + "jest-diff": "^29.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.12.0 || ^20.9.0 || ^22.11.0 || >=23.0.0" }, "peerDependencies": { - "jest": ">=27.2.5" + "jest": ">=27.2.5", + "typescript": ">=5.0.0" }, "peerDependenciesMeta": { "jest": { "optional": true + }, + "typescript": { + "optional": false } } }, @@ -9987,6 +10896,22 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-runtime/node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/jest-snapshot": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", @@ -10520,12 +11445,12 @@ } }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/memory-pager": { @@ -10535,10 +11460,13 @@ "license": "MIT" }, "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", + "engines": { + "node": ">=18" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -10595,21 +11523,21 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { "node": ">= 0.6" @@ -10868,9 +11796,9 @@ } }, "node_modules/mongodb-download-url": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/mongodb-download-url/-/mongodb-download-url-1.5.7.tgz", - "integrity": "sha512-GpQJAfYmfYwqVXUyfIUQXe5kFoiCK5kORBJnPixAmQGmabZix6fBTpX7vSy3J46VgiAe+VEOjSikK/TcGKTw+A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mongodb-download-url/-/mongodb-download-url-1.6.0.tgz", + "integrity": "sha512-IQcQfKi3zdejKIAPaCpsW2F1FpMBsdifzY5K0YdddmJSFDJAGY7xUbCVm0pdL36+1ck6+c2ytTSqzProLhvGoA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -10955,14 +11883,14 @@ "license": "Apache-2.0" }, "node_modules/mongodb-runner": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/mongodb-runner/-/mongodb-runner-5.8.2.tgz", - "integrity": "sha512-Fsr87S3P75jAd/D1ly0/lODpjtFpHd+q9Ml2KjQQmPeGisdjCDO9bFOJ1F9xrdtvww2eeR8xKBXrtZYdP5P59A==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/mongodb-runner/-/mongodb-runner-5.9.0.tgz", + "integrity": "sha512-31oyYEmLvkuqDV9J9kuwwOE2SHV1LaPyqr+fZsgYT56ceynqq4ABUOXmmdZBXm3zdLM1QGUft5UJokkkhXGPCQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@mongodb-js/mongodb-downloader": "^0.3.9", - "@mongodb-js/saslprep": "^1.2.2", + "@mongodb-js/mongodb-downloader": "^0.4.0", + "@mongodb-js/saslprep": "^1.3.0", "debug": "^4.4.0", "mongodb": "^6.9.0", "mongodb-connection-string-url": "^3.0.0", @@ -11006,6 +11934,16 @@ "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", @@ -11063,6 +12001,16 @@ "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", @@ -11102,31 +12050,6 @@ "license": "MIT", "optional": true }, - "node_modules/native-machine-id": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/native-machine-id/-/native-machine-id-0.0.8.tgz", - "integrity": "sha512-0sMw6WHfG1A7N59C1odmge9K/F9uC+1dgXHjMW57w319ii/nI05FDFwlXSjPMAHHB7hU7OInpVuH+Sgjz5enog==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^8.0.0" - }, - "bin": { - "native-machine-id": "dist/bin/machine-id.js" - } - }, - "node_modules/native-machine-id/node_modules/node-addon-api": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", - "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -11135,9 +12058,9 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -11237,6 +12160,12 @@ "dev": true, "license": "MIT" }, + "node_modules/node-machine-id": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", + "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", + "license": "MIT" + }, "node_modules/node-readfiles": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", @@ -11485,9 +12414,9 @@ } }, "node_modules/openapi-fetch": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.13.5.tgz", - "integrity": "sha512-AQK8T9GSKFREFlN1DBXTYsLjs7YV2tZcJ7zUWxbjMoQmj8dDSFRrzhLCbHPZWA1TMV3vACqfCxLEZcwf2wxV6Q==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.14.0.tgz", + "integrity": "sha512-PshIdm1NgdLvb05zp8LqRQMNSKzIlPkyMxYFxwyHR+UlKD4t2nUjkDhNxeRbhRSEd3x5EUNh2w5sJYwkhOH4fg==", "license": "MIT", "dependencies": { "openapi-typescript-helpers": "^0.0.15" @@ -11532,17 +12461,17 @@ "license": "MIT" }, "node_modules/openapi-typescript": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.6.1.tgz", - "integrity": "sha512-F7RXEeo/heF3O9lOXo2bNjCOtfp7u+D6W3a3VNEH2xE6v+fxLtn5nq0uvUcA1F5aT+CMhNeC5Uqtg5tlXFX/ag==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.8.0.tgz", + "integrity": "sha512-1EeVWmDzi16A+siQlo/SwSGIT7HwaFAVjvMA7/jG5HMLSnrUOzPL7uSTRZZa4v/LCRxHTApHKtNY6glApEoiUQ==", "dev": true, "license": "MIT", "dependencies": { - "@redocly/openapi-core": "^1.28.0", + "@redocly/openapi-core": "^1.34.3", "ansi-colors": "^4.1.3", "change-case": "^5.4.4", - "parse-json": "^8.1.0", - "supports-color": "^9.4.0", + "parse-json": "^8.3.0", + "supports-color": "^10.0.0", "yargs-parser": "^21.1.1" }, "bin": { @@ -11577,22 +12506,22 @@ } }, "node_modules/openapi-typescript/node_modules/supports-color": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", - "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.0.0.tgz", + "integrity": "sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/openapi-typescript/node_modules/type-fest": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz", - "integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.1.tgz", + "integrity": "sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -11602,6 +12531,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/openid-client": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", @@ -11841,10 +12780,13 @@ "license": "MIT" }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } }, "node_modules/pend": { "version": "1.2.0", @@ -12114,6 +13056,19 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -12278,12 +13233,12 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -12347,18 +13302,6 @@ "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -12420,9 +13363,9 @@ "license": "MIT" }, "node_modules/react-remove-scroll": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", - "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", "dev": true, "license": "MIT", "dependencies": { @@ -12545,16 +13488,16 @@ } }, "node_modules/redoc": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.4.0.tgz", - "integrity": "sha512-rFlfzFVWS9XJ6aYAs/bHnLhHP5FQEhwAHDBVgwb9L2FqDQ8Hu8rQ1G84iwaWXxZfPP9UWn7JdWkxI6MXr2ZDjw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.5.0.tgz", + "integrity": "sha512-NpYsOZ1PD9qFdjbLVBZJWptqE+4Y6TkUuvEOqPUmoH7AKOmPcE+hYjotLxQNTqVoWL4z0T2uxILmcc8JGDci+Q==", "dev": true, "license": "MIT", "dependencies": { "@redocly/openapi-core": "^1.4.0", "classnames": "^2.3.2", "decko": "^1.2.0", - "dompurify": "^3.0.6", + "dompurify": "^3.2.4", "eventemitter3": "^5.0.1", "json-pointer": "^0.6.2", "lunr": "^2.3.9", @@ -12595,13 +13538,6 @@ "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true, - "license": "MIT" - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -12742,15 +13678,6 @@ "node": ">= 18" } }, - "node_modules/router/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==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, "node_modules/run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", @@ -12850,10 +13777,17 @@ "seek-table": "bin/seek-bzip-table" } }, + "node_modules/seek-bzip/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "devOptional": true, "license": "ISC", "bin": { @@ -12864,51 +13798,25 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "license": "MIT", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" + "node": ">= 18" } }, "node_modules/serve-handler": { @@ -12928,9 +13836,9 @@ } }, "node_modules/serve-handler/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "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": { @@ -13012,18 +13920,18 @@ } }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "license": "MIT", "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" } }, "node_modules/set-cookie-parser": { @@ -13624,9 +14532,9 @@ "license": "MIT" }, "node_modules/styled-components": { - "version": "6.1.17", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.17.tgz", - "integrity": "sha512-97D7DwWanI7nN24v0D4SvbfjLE9656umNSJZkBkDIWL37aZqG/wRQ+Y9pWtXyBIM/NSfcBzHLErEsqHmJNSVUg==", + "version": "6.1.18", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.18.tgz", + "integrity": "sha512-Mvf3gJFzZCkhjY2Y/Fx9z1m3dxbza0uI9H1CbNZm/jSHCojzJhQ0R7bByrlFJINnMzz/gPulpoFFGymNwrsMcw==", "dev": true, "license": "MIT", "dependencies": { @@ -13776,6 +14684,22 @@ "node": ">= 6" } }, + "node_modules/synckit": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.4" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, "node_modules/system-ca": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/system-ca/-/system-ca-2.0.1.tgz", @@ -13798,9 +14722,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", - "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz", + "integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==", "dev": true, "license": "MIT", "peer": true @@ -13834,9 +14758,9 @@ } }, "node_modules/tar-fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", - "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", "license": "MIT", "optional": true, "dependencies": { @@ -14064,21 +14988,20 @@ } }, "node_modules/ts-jest": { - "version": "29.3.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.2.tgz", - "integrity": "sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug==", + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", + "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", "dev": true, "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.7.1", - "type-fest": "^4.39.1", + "semver": "^7.7.2", + "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, "bin": { @@ -14089,10 +15012,11 @@ }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { @@ -14110,13 +15034,16 @@ }, "esbuild": { "optional": true + }, + "jest-util": { + "optional": true } } }, "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz", - "integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -14126,6 +15053,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ts-jest/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/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -14177,9 +15114,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz", - "integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==", + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14252,13 +15189,14 @@ } }, "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { "node": ">= 0.6" @@ -14286,15 +15224,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.0.tgz", - "integrity": "sha512-u+93F0sB0An8WEAPtwxVhFby573E8ckdjwUUQUj9QA4v8JAvgtoDdIyYR3XFwFHq2W1KJ1AurwJCO+w+Y1ixyQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.0.tgz", + "integrity": "sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.31.0", - "@typescript-eslint/parser": "8.31.0", - "@typescript-eslint/utils": "8.31.0" + "@typescript-eslint/eslint-plugin": "8.34.0", + "@typescript-eslint/parser": "8.34.0", + "@typescript-eslint/utils": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14403,7 +15341,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -14672,9 +15609,9 @@ } }, "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", "dev": true, "license": "MIT", "engines": { @@ -14720,16 +15657,16 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } }, "node_modules/yaml-ast-parser": { @@ -14759,12 +15696,12 @@ } }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "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==", "license": "ISC", "engines": { - "node": ">=12" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yargs/node_modules/yargs-parser": { @@ -14812,9 +15749,9 @@ } }, "node_modules/zod": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "version": "3.25.49", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.49.tgz", + "integrity": "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index c9e52549..54d28a9c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mongodb-mcp-server", "description": "MongoDB Model Context Protocol Server", - "version": "0.0.4", + "version": "0.1.2", "main": "dist/index.js", "author": "MongoDB ", "homepage": "https://github.com/mongodb-js/mongodb-mcp-server", @@ -18,36 +18,38 @@ "scripts": { "prepare": "npm run build", "build:clean": "rm -rf dist", - "build:compile": "tsc", + "build:compile": "tsc --project tsconfig.build.json", "build:chmod": "chmod +x dist/index.js", "build": "npm run build:clean && npm run build:compile && npm run build:chmod", "inspect": "npm run build && mcp-inspector -- dist/index.js", "prettier": "prettier", - "check": "npm run build && npm run check:lint && npm run check:format", + "check": "npm run build && npm run check:types && npm run check:lint && npm run check:format", "check:lint": "eslint .", "check:format": "prettier -c .", + "check:types": "tsc --noEmit --project tsconfig.json", "reformat": "prettier --write .", "generate": "./scripts/generate.sh", - "test": "jest --coverage" + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage" }, "license": "Apache-2.0", "devDependencies": { "@eslint/js": "^9.24.0", - "@jest/globals": "^29.7.0", - "@modelcontextprotocol/inspector": "^0.8.2", + "@jest/globals": "^30.0.0", + "@modelcontextprotocol/inspector": "^0.14.0", "@redocly/cli": "^1.34.2", "@types/jest": "^29.5.14", "@types/node": "^22.14.0", "@types/simple-oauth2": "^5.0.7", "@types/yargs-parser": "^21.0.3", "eslint": "^9.24.0", - "eslint-config-prettier": "^10.1.1", + "eslint-config-prettier": "^10.1.2", + "eslint-plugin-jest": "^28.11.0", + "eslint-plugin-prettier": "^5.2.6", "globals": "^16.0.0", "jest": "^29.7.0", "jest-environment-node": "^29.7.0", - "jest-extended": "^4.0.2", + "jest-extended": "^6.0.0", "mongodb-runner": "^5.8.2", - "native-machine-id": "^0.0.8", "openapi-types": "^12.1.3", "openapi-typescript": "^7.6.1", "prettier": "^3.5.3", @@ -58,21 +60,24 @@ "yaml": "^2.7.1" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.8.0", + "@modelcontextprotocol/sdk": "^1.11.2", + "@mongodb-js/device-id": "^0.2.1", "@mongodb-js/devtools-connect": "^3.7.2", "@mongosh/service-provider-node-driver": "^3.6.0", "bson": "^6.10.3", "lru-cache": "^11.1.0", "mongodb": "^6.15.0", + "mongodb-connection-string-url": "^3.0.2", "mongodb-log-writer": "^2.4.1", "mongodb-redact": "^1.1.6", "mongodb-schema": "^12.6.2", - "openapi-fetch": "^0.13.5", + "node-machine-id": "1.1.12", + "openapi-fetch": "^0.14.0", "simple-oauth2": "^5.1.0", - "yargs-parser": "^21.1.1", + "yargs-parser": "^22.0.0", "zod": "^3.24.2" }, "engines": { - "node": ">=20.0.0" + "node": ">=20.10.0" } } diff --git a/scripts/apply.ts b/scripts/apply.ts index 420a31d0..c051c4ee 100755 --- a/scripts/apply.ts +++ b/scripts/apply.ts @@ -9,13 +9,15 @@ function findObjectFromRef(obj: T | OpenAPIV3_1.ReferenceObject, openapi: Ope } const paramParts = ref.split("/"); paramParts.shift(); // Remove the first part which is always '#' - let foundObj: any = openapi; // eslint-disable-line @typescript-eslint/no-explicit-any + + let foundObj: Record = openapi; while (true) { const part = paramParts.shift(); if (!part) { break; } - foundObj = foundObj[part]; + + foundObj = foundObj[part] as Record; } return foundObj as T; } @@ -28,7 +30,7 @@ async function main() { process.exit(1); } - const specFile = (await fs.readFile(spec, "utf8")) as string; + const specFile = await fs.readFile(spec as string, "utf8"); const operations: { path: string; @@ -42,7 +44,8 @@ async function main() { const openapi = JSON.parse(specFile) as OpenAPIV3_1.Document; for (const path in openapi.paths) { for (const method in openapi.paths[path]) { - const operation: OpenAPIV3_1.OperationObject = openapi.paths[path][method]; + // @ts-expect-error This is a workaround for the OpenAPI types + const operation = openapi.paths[path][method] as OpenAPIV3_1.OperationObject; if (!operation.operationId || !operation.tags?.length) { continue; @@ -55,11 +58,11 @@ async function main() { const httpCode = parseInt(code, 10); if (httpCode >= 200 && httpCode < 300) { const response = operation.responses[code]; - const responseObject = findObjectFromRef(response, openapi); - if (responseObject.content) { + const responseObject = findObjectFromRef(response, openapi) as OpenAPIV3_1.ResponseObject; + if (responseObject && responseObject.content) { for (const contentType in responseObject.content) { const content = responseObject.content[contentType]; - hasResponseBody = !!content.schema; + hasResponseBody = !!content?.schema; } } } @@ -81,7 +84,7 @@ async function main() { operationId: operation.operationId || "", requiredParams, hasResponseBody, - tag: operation.tags[0], + tag: operation.tags?.[0] ?? "", }); } } @@ -90,7 +93,10 @@ async function main() { .map((operation) => { const { operationId, method, path, requiredParams, hasResponseBody } = operation; return `async ${operationId}(options${requiredParams ? "" : "?"}: FetchOptions) { - ${hasResponseBody ? `const { data } = ` : ``}await this.client.${method}("${path}", options); + const { ${hasResponseBody ? `data, ` : ``}error, response } = await this.client.${method}("${path}", options); + if (error) { + throw ApiClientError.fromError(response, error); + } ${ hasResponseBody ? `return data; @@ -101,9 +107,9 @@ async function main() { }) .join("\n"); - const templateFile = (await fs.readFile(file, "utf8")) as string; + const templateFile = await fs.readFile(file as string, "utf8"); const templateLines = templateFile.split("\n"); - let outputLines: string[] = []; + const outputLines: string[] = []; let addLines = true; for (const line of templateLines) { if (line.includes("DO NOT EDIT. This is auto-generated code.")) { @@ -120,7 +126,7 @@ async function main() { } const output = outputLines.join("\n"); - await fs.writeFile(file, output, "utf8"); + await fs.writeFile(file as string, output, "utf8"); } main().catch((error) => { diff --git a/scripts/filter.ts b/scripts/filter.ts index 34a69f7a..06340078 100755 --- a/scripts/filter.ts +++ b/scripts/filter.ts @@ -8,6 +8,7 @@ async function readStdin() { reject(err); }); process.stdin.on("data", (chunk) => { + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands data += chunk; }); process.stdin.on("end", () => { @@ -24,9 +25,13 @@ function filterOpenapi(openapi: OpenAPIV3_1.Document): OpenAPIV3_1.Document { "createProject", "deleteProject", "listClusters", + "listFlexClusters", "getCluster", + "getFlexCluster", "createCluster", + "createFlexCluster", "deleteCluster", + "deleteFlexCluster", "listClustersForAllProjects", "createDatabaseUser", "deleteDatabaseUser", @@ -35,6 +40,7 @@ function filterOpenapi(openapi: OpenAPIV3_1.Document): OpenAPIV3_1.Document { "createProjectIpAccessList", "deleteProjectIpAccessList", "listOrganizationProjects", + "listAlerts", ]; const filteredPaths = {}; @@ -42,11 +48,14 @@ function filterOpenapi(openapi: OpenAPIV3_1.Document): OpenAPIV3_1.Document { for (const path in openapi.paths) { const filteredMethods = {} as OpenAPIV3_1.PathItemObject; for (const method in openapi.paths[path]) { - if (allowedOperations.includes(openapi.paths[path][method].operationId)) { - filteredMethods[method] = openapi.paths[path][method]; + // @ts-expect-error This is a workaround for the OpenAPI types + if (allowedOperations.includes((openapi.paths[path][method] as { operationId: string }).operationId)) { + // @ts-expect-error This is a workaround for the OpenAPI types + filteredMethods[method] = openapi.paths[path][method] as OpenAPIV3_1.OperationObject; } } if (Object.keys(filteredMethods).length > 0) { + // @ts-expect-error This is a workaround for the OpenAPI types filteredPaths[path] = filteredMethods; } } diff --git a/src/common/atlas/apiClient.ts b/src/common/atlas/apiClient.ts index f71e1162..1773aba5 100644 --- a/src/common/atlas/apiClient.ts +++ b/src/common/atlas/apiClient.ts @@ -3,10 +3,8 @@ import type { FetchOptions } from "openapi-fetch"; import { AccessToken, ClientCredentials } from "simple-oauth2"; import { ApiClientError } from "./apiClientError.js"; import { paths, operations } from "./openapi.js"; -import { BaseEvent } from "../../telemetry/types.js"; -import { mongoLogId } from "mongodb-log-writer"; -import logger from "../../logger.js"; -import { packageInfo } from "../../packageInfo.js"; +import { CommonProperties, TelemetryEvent } from "../../telemetry/types.js"; +import { packageInfo } from "../../helpers/packageInfo.js"; const ATLAS_API_VERSION = "2025-03-12"; @@ -17,7 +15,7 @@ export interface ApiClientCredentials { export interface ApiClientOptions { credentials?: ApiClientCredentials; - baseUrl?: string; + baseUrl: string; userAgent?: string; } @@ -36,7 +34,9 @@ export class ApiClient { private getAccessToken = async () => { if (this.oauth2Client && (!this.accessToken || this.accessToken.expired())) { - this.accessToken = await this.oauth2Client.getToken({}); + this.accessToken = await this.oauth2Client.getToken({ + agent: this.options.userAgent, + }); } return this.accessToken?.token.access_token as string | undefined; }; @@ -57,20 +57,11 @@ export class ApiClient { }, }; - private readonly errorMiddleware: Middleware = { - async onResponse({ response }) { - if (!response.ok) { - throw await ApiClientError.fromResponse(response); - } - }, - }; - - constructor(options?: ApiClientOptions) { + constructor(options: ApiClientOptions) { this.options = { ...options, - baseUrl: options?.baseUrl || "https://cloud.mongodb.com/", userAgent: - options?.userAgent || + options.userAgent || `AtlasMCP/${packageInfo.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`, }; @@ -94,18 +85,16 @@ export class ApiClient { }); this.client.use(this.authMiddleware); } - this.client.use(this.errorMiddleware); } public hasCredentials(): boolean { - logger.info( - mongoLogId(1_000_000), - "api-client", - `Checking if API client has credentials: ${!!(this.oauth2Client && this.accessToken)}` - ); return !!(this.oauth2Client && this.accessToken); } + public async validateAccessToken(): Promise { + await this.getAccessToken(); + } + public async getIpInfo(): Promise<{ currentIpv4Address: string; }> { @@ -131,22 +120,59 @@ export class ApiClient { }>; } - async sendEvents(events: BaseEvent[]): Promise { - let endpoint = "api/private/unauth/telemetry/events"; + public async sendEvents(events: TelemetryEvent[]): Promise { + if (!this.options.credentials) { + await this.sendUnauthEvents(events); + return; + } + + try { + await this.sendAuthEvents(events); + } catch (error) { + if (error instanceof ApiClientError) { + if (error.response.status !== 401) { + throw error; + } + } + + // send unauth events if any of the following are true: + // 1: the token is not valid (not ApiClientError) + // 2: if the api responded with 401 (ApiClientError with status 401) + await this.sendUnauthEvents(events); + } + } + + private async sendAuthEvents(events: TelemetryEvent[]): Promise { + const accessToken = await this.getAccessToken(); + if (!accessToken) { + throw new Error("No access token available"); + } + const authUrl = new URL("api/private/v1.0/telemetry/events", this.options.baseUrl); + const response = await fetch(authUrl, { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + "User-Agent": this.options.userAgent, + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify(events), + }); + + if (!response.ok) { + throw await ApiClientError.fromResponse(response); + } + } + + private async sendUnauthEvents(events: TelemetryEvent[]): Promise { const headers: Record = { Accept: "application/json", "Content-Type": "application/json", "User-Agent": this.options.userAgent, }; - const accessToken = await this.getAccessToken(); - if (accessToken) { - endpoint = "api/private/v1.0/telemetry/events"; - headers["Authorization"] = `Bearer ${accessToken}`; - } - - const url = new URL(endpoint, this.options.baseUrl); - const response = await fetch(url, { + const unauthUrl = new URL("api/private/unauth/telemetry/events", this.options.baseUrl); + const response = await fetch(unauthUrl, { method: "POST", headers, body: JSON.stringify(events), @@ -159,83 +185,201 @@ export class ApiClient { // DO NOT EDIT. This is auto-generated code. async listClustersForAllProjects(options?: FetchOptions) { - const { data } = await this.client.GET("/api/atlas/v2/clusters", options); + const { data, error, response } = await this.client.GET("/api/atlas/v2/clusters", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async listProjects(options?: FetchOptions) { - const { data } = await this.client.GET("/api/atlas/v2/groups", options); + const { data, error, response } = await this.client.GET("/api/atlas/v2/groups", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async createProject(options: FetchOptions) { - const { data } = await this.client.POST("/api/atlas/v2/groups", options); + const { data, error, response } = await this.client.POST("/api/atlas/v2/groups", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async deleteProject(options: FetchOptions) { - await this.client.DELETE("/api/atlas/v2/groups/{groupId}", options); + const { error, response } = await this.client.DELETE("/api/atlas/v2/groups/{groupId}", options); + if (error) { + throw ApiClientError.fromError(response, error); + } } async getProject(options: FetchOptions) { - const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}", options); + const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async listProjectIpAccessLists(options: FetchOptions) { - const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/accessList", options); + const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}/accessList", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async createProjectIpAccessList(options: FetchOptions) { - const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/accessList", options); + const { data, error, response } = await this.client.POST("/api/atlas/v2/groups/{groupId}/accessList", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async deleteProjectIpAccessList(options: FetchOptions) { - await this.client.DELETE("/api/atlas/v2/groups/{groupId}/accessList/{entryValue}", options); + const { error, response } = await this.client.DELETE( + "/api/atlas/v2/groups/{groupId}/accessList/{entryValue}", + options + ); + if (error) { + throw ApiClientError.fromError(response, error); + } + } + + async listAlerts(options: FetchOptions) { + const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}/alerts", options); + if (error) { + throw ApiClientError.fromError(response, error); + } + return data; } async listClusters(options: FetchOptions) { - const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters", options); + const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async createCluster(options: FetchOptions) { - const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/clusters", options); + const { data, error, response } = await this.client.POST("/api/atlas/v2/groups/{groupId}/clusters", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async deleteCluster(options: FetchOptions) { - await this.client.DELETE("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options); + const { error, response } = await this.client.DELETE( + "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", + options + ); + if (error) { + throw ApiClientError.fromError(response, error); + } } async getCluster(options: FetchOptions) { - const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options); + const { data, error, response } = await this.client.GET( + "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", + options + ); + + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async listDatabaseUsers(options: FetchOptions) { - const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/databaseUsers", options); + const { data, error, response } = await this.client.GET( + "/api/atlas/v2/groups/{groupId}/databaseUsers", + options + ); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async createDatabaseUser(options: FetchOptions) { - const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/databaseUsers", options); + const { data, error, response } = await this.client.POST( + "/api/atlas/v2/groups/{groupId}/databaseUsers", + options + ); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async deleteDatabaseUser(options: FetchOptions) { - await this.client.DELETE("/api/atlas/v2/groups/{groupId}/databaseUsers/{databaseName}/{username}", options); + const { error, response } = await this.client.DELETE( + "/api/atlas/v2/groups/{groupId}/databaseUsers/{databaseName}/{username}", + options + ); + if (error) { + throw ApiClientError.fromError(response, error); + } + } + + async listFlexClusters(options: FetchOptions) { + const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}/flexClusters", options); + if (error) { + throw ApiClientError.fromError(response, error); + } + return data; + } + + async createFlexCluster(options: FetchOptions) { + const { data, error, response } = await this.client.POST( + "/api/atlas/v2/groups/{groupId}/flexClusters", + options + ); + if (error) { + throw ApiClientError.fromError(response, error); + } + return data; + } + + async deleteFlexCluster(options: FetchOptions) { + const { error, response } = await this.client.DELETE( + "/api/atlas/v2/groups/{groupId}/flexClusters/{name}", + options + ); + if (error) { + throw ApiClientError.fromError(response, error); + } + } + + async getFlexCluster(options: FetchOptions) { + const { data, error, response } = await this.client.GET( + "/api/atlas/v2/groups/{groupId}/flexClusters/{name}", + options + ); + if (error) { + throw ApiClientError.fromError(response, error); + } + return data; } async listOrganizations(options?: FetchOptions) { - const { data } = await this.client.GET("/api/atlas/v2/orgs", options); + const { data, error, response } = await this.client.GET("/api/atlas/v2/orgs", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } async listOrganizationProjects(options: FetchOptions) { - const { data } = await this.client.GET("/api/atlas/v2/orgs/{orgId}/groups", options); + const { data, error, response } = await this.client.GET("/api/atlas/v2/orgs/{orgId}/groups", options); + if (error) { + throw ApiClientError.fromError(response, error); + } return data; } diff --git a/src/common/atlas/apiClientError.ts b/src/common/atlas/apiClientError.ts index 6073c161..baea7b57 100644 --- a/src/common/atlas/apiClientError.ts +++ b/src/common/atlas/apiClientError.ts @@ -1,21 +1,72 @@ -export class ApiClientError extends Error { - response?: Response; +import { ApiError } from "./openapi.js"; - constructor(message: string, response: Response | undefined = undefined) { +export class ApiClientError extends Error { + private constructor( + message: string, + public readonly response: Response, + public readonly apiError?: ApiError + ) { super(message); this.name = "ApiClientError"; - this.response = response; } static async fromResponse( response: Response, message: string = `error calling Atlas API` ): Promise { + const err = await this.extractError(response); + + return this.fromError(response, err, message); + } + + static fromError( + response: Response, + error?: ApiError | string | Error, + message: string = `error calling Atlas API` + ): ApiClientError { + const errorMessage = this.buildErrorMessage(error); + + const apiError = typeof error === "object" && !(error instanceof Error) ? error : undefined; + + return new ApiClientError( + `[${response.status} ${response.statusText}] ${message}: ${errorMessage}`, + response, + apiError + ); + } + + private static async extractError(response: Response): Promise { try { - const text = await response.text(); - return new ApiClientError(`${message}: [${response.status} ${response.statusText}] ${text}`, response); + return (await response.json()) as ApiError; } catch { - return new ApiClientError(`${message}: ${response.status} ${response.statusText}`, response); + try { + return await response.text(); + } catch { + return undefined; + } } } + + private static buildErrorMessage(error?: string | ApiError | Error): string { + let errorMessage: string = "unknown error"; + + if (error instanceof Error) { + return error.message; + } + + //eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check + switch (typeof error) { + case "object": + errorMessage = error.reason || "unknown error"; + if (error.detail && error.detail.length > 0) { + errorMessage = `${errorMessage}; ${error.detail}`; + } + break; + case "string": + errorMessage = error; + break; + } + + return errorMessage.trim(); + } } diff --git a/src/common/atlas/cluster.ts b/src/common/atlas/cluster.ts new file mode 100644 index 00000000..793cd99b --- /dev/null +++ b/src/common/atlas/cluster.ts @@ -0,0 +1,94 @@ +import { ClusterDescription20240805, FlexClusterDescription20241113 } from "./openapi.js"; +import { ApiClient } from "./apiClient.js"; +import logger, { LogId } from "../../logger.js"; + +export interface Cluster { + name?: string; + instanceType: "FREE" | "DEDICATED" | "FLEX"; + instanceSize?: string; + state?: "IDLE" | "CREATING" | "UPDATING" | "DELETING" | "REPAIRING"; + mongoDBVersion?: string; + connectionString?: string; +} + +export function formatFlexCluster(cluster: FlexClusterDescription20241113): Cluster { + return { + name: cluster.name, + instanceType: "FLEX", + instanceSize: undefined, + state: cluster.stateName, + mongoDBVersion: cluster.mongoDBVersion, + connectionString: cluster.connectionStrings?.standardSrv || cluster.connectionStrings?.standard, + }; +} + +export function formatCluster(cluster: ClusterDescription20240805): Cluster { + const regionConfigs = (cluster.replicationSpecs || []) + .map( + (replicationSpec) => + (replicationSpec.regionConfigs || []) as { + providerName: string; + electableSpecs?: { + instanceSize: string; + }; + readOnlySpecs?: { + instanceSize: string; + }; + analyticsSpecs?: { + instanceSize: string; + }; + }[] + ) + .flat() + .map((regionConfig) => { + return { + providerName: regionConfig.providerName, + instanceSize: + regionConfig.electableSpecs?.instanceSize || + regionConfig.readOnlySpecs?.instanceSize || + regionConfig.analyticsSpecs?.instanceSize, + }; + }); + + const instanceSize = regionConfigs[0]?.instanceSize ?? "UNKNOWN"; + const clusterInstanceType = instanceSize == "M0" ? "FREE" : "DEDICATED"; + + return { + name: cluster.name, + instanceType: clusterInstanceType, + instanceSize: clusterInstanceType == "DEDICATED" ? instanceSize : undefined, + state: cluster.stateName, + mongoDBVersion: cluster.mongoDBVersion, + connectionString: cluster.connectionStrings?.standardSrv || cluster.connectionStrings?.standard, + }; +} + +export async function inspectCluster(apiClient: ApiClient, projectId: string, clusterName: string): Promise { + try { + const cluster = await apiClient.getCluster({ + params: { + path: { + groupId: projectId, + clusterName, + }, + }, + }); + return formatCluster(cluster); + } catch (error) { + try { + const cluster = await apiClient.getFlexCluster({ + params: { + path: { + groupId: projectId, + name: clusterName, + }, + }, + }); + return formatFlexCluster(cluster); + } catch (flexError) { + const err = flexError instanceof Error ? flexError : new Error(String(flexError)); + logger.error(LogId.atlasInspectFailure, "inspect-cluster", `error inspecting cluster: ${err.message}`); + throw error; + } + } +} diff --git a/src/common/atlas/generatePassword.ts b/src/common/atlas/generatePassword.ts new file mode 100644 index 00000000..9e07267c --- /dev/null +++ b/src/common/atlas/generatePassword.ts @@ -0,0 +1,10 @@ +import { randomBytes } from "crypto"; +import { promisify } from "util"; + +const randomBytesAsync = promisify(randomBytes); + +export async function generateSecurePassword(): Promise { + const buf = await randomBytesAsync(16); + const pass = buf.toString("base64url"); + return pass; +} diff --git a/src/common/atlas/openapi.d.ts b/src/common/atlas/openapi.d.ts index 3534bf93..dbc88950 100644 --- a/src/common/atlas/openapi.d.ts +++ b/src/common/atlas/openapi.d.ts @@ -116,6 +116,28 @@ export interface paths { patch?: never; trace?: never; }; + "/api/atlas/v2/groups/{groupId}/alerts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Return All Alerts from One Project + * @description Returns all alerts. These alerts apply to all components in one project. You receive an alert when a monitored component meets or exceeds a value you set. To use this resource, the requesting Service Account or API Key must have the Project Read Only role. + * + * This resource remains under revision and may change. + */ + get: operations["listAlerts"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/atlas/v2/groups/{groupId}/clusters": { parameters: { query?: never; @@ -216,6 +238,54 @@ export interface paths { patch?: never; trace?: never; }; + "/api/atlas/v2/groups/{groupId}/flexClusters": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Return All Flex Clusters from One Project + * @description Returns details for all flex clusters in the specified project. To use this resource, the requesting Service Account or API Key must have the Project Read Only role. + */ + get: operations["listFlexClusters"]; + put?: never; + /** + * Create One Flex Cluster in One Project + * @description Creates one flex cluster in the specified project. To use this resource, the requesting Service Account or API Key must have the Project Owner role. + */ + post: operations["createFlexCluster"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/atlas/v2/groups/{groupId}/flexClusters/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Return One Flex Cluster from One Project + * @description Returns details for one flex cluster in the specified project. To use this resource, the requesting Service Account or API Key must have the Project Read Only role. + */ + get: operations["getFlexCluster"]; + put?: never; + post?: never; + /** + * Remove One Flex Cluster from One Project + * @description Removes one flex cluster from the specified project. The flex cluster must have termination protection disabled in order to be deleted. To use this resource, the requesting Service Account or API Key must have the Project Owner role. + */ + delete: operations["deleteFlexCluster"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/atlas/v2/orgs": { parameters: { query?: never; @@ -580,6 +650,7 @@ export interface components { /** @description Flag that indicates whether the instance size may scale down via reactive auto-scaling. MongoDB Cloud requires this parameter if **replicationSpecs[n].regionConfigs[m].autoScaling.compute.enabled** is `true`. If you enable this option, specify a value for **replicationSpecs[n].regionConfigs[m].autoScaling.compute.minInstanceSize**. */ scaleDownEnabled?: boolean; }; + AlertViewForNdsGroup: components["schemas"]["AppServiceAlertView"] | components["schemas"]["ClusterAlertView"] | components["schemas"]["HostAlertViewForNdsGroup"] | components["schemas"]["HostMetricAlert"] | components["schemas"]["ReplicaSetAlertViewForNdsGroup"] | components["schemas"]["StreamProcessorAlertViewForNdsGroup"] | components["schemas"]["DefaultAlertViewForNdsGroup"]; /** @description Object that contains the identifying characteristics of the Amazon Web Services (AWS) Key Management Service (KMS). This field always returns a null value. */ ApiAtlasCloudProviderAccessFeatureUsageFeatureIdView: Record | null; /** @description Group of settings that configures a subset of the advanced configuration details. */ @@ -649,6 +720,87 @@ export interface components { /** @description Application error message returned with this error. */ readonly reason?: string; }; + /** + * App Services Alerts + * @description App Services alert notifies different activities about a BAAS application. + */ + AppServiceAlertView: { + /** + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. + */ + acknowledgedUntil?: string; + /** + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. + */ + acknowledgementComment?: string; + /** + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. + */ + readonly acknowledgingUsername?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly alertConfigId: string; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly created: string; + eventTypeName: components["schemas"]["AppServiceEventTypeViewAlertable"]; + /** + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly id: string; + /** + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. + */ + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly orgId?: string; + /** + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. + */ + readonly resolved?: string; + /** + * @description State of this alert at the time you requested its details. + * @example OPEN + * @enum {string} + */ + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; + }; + /** + * App Services Event Types + * @description Incident that triggered this alert. + * @example DEPLOYMENT_FAILURE + * @enum {string} + */ + AppServiceEventTypeViewAlertable: "URL_CONFIRMATION" | "SUCCESSFUL_DEPLOY" | "DEPLOYMENT_FAILURE" | "DEPLOYMENT_MODEL_CHANGE_SUCCESS" | "DEPLOYMENT_MODEL_CHANGE_FAILURE" | "REQUEST_RATE_LIMIT" | "LOG_FORWARDER_FAILURE" | "OUTSIDE_REALM_METRIC_THRESHOLD" | "SYNC_FAILURE" | "TRIGGER_FAILURE" | "TRIGGER_AUTO_RESUMED"; /** @description Details that describe the organization. */ AtlasOrganization: { /** @@ -715,7 +867,7 @@ export interface components { * @description Azure region to which MongoDB Cloud deployed this network peering container. * @enum {string} */ - region: "US_CENTRAL" | "US_EAST" | "US_EAST_2" | "US_EAST_2_EUAP" | "US_NORTH_CENTRAL" | "US_WEST" | "US_SOUTH_CENTRAL" | "EUROPE_NORTH" | "EUROPE_WEST" | "US_WEST_CENTRAL" | "US_WEST_2" | "US_WEST_3" | "CANADA_EAST" | "CANADA_CENTRAL" | "BRAZIL_SOUTH" | "BRAZIL_SOUTHEAST" | "AUSTRALIA_EAST" | "AUSTRALIA_SOUTH_EAST" | "AUSTRALIA_CENTRAL" | "AUSTRALIA_CENTRAL_2" | "UAE_NORTH" | "GERMANY_CENTRAL" | "GERMANY_NORTH_EAST" | "GERMANY_WEST_CENTRAL" | "GERMANY_NORTH" | "SWITZERLAND_NORTH" | "SWITZERLAND_WEST" | "SWEDEN_CENTRAL" | "SWEDEN_SOUTH" | "UK_SOUTH" | "UK_WEST" | "INDIA_CENTRAL" | "INDIA_WEST" | "INDIA_SOUTH" | "CHINA_EAST" | "CHINA_NORTH" | "ASIA_EAST" | "JAPAN_EAST" | "JAPAN_WEST" | "ASIA_SOUTH_EAST" | "KOREA_CENTRAL" | "KOREA_SOUTH" | "FRANCE_CENTRAL" | "FRANCE_SOUTH" | "SOUTH_AFRICA_NORTH" | "SOUTH_AFRICA_WEST" | "NORWAY_EAST" | "NORWAY_WEST" | "UAE_CENTRAL" | "QATAR_CENTRAL" | "POLAND_CENTRAL" | "ISRAEL_CENTRAL" | "ITALY_NORTH" | "SPAIN_CENTRAL" | "MEXICO_CENTRAL" | "NEW_ZEALAND_NORTH"; + region: "US_CENTRAL" | "US_EAST" | "US_EAST_2" | "US_EAST_2_EUAP" | "US_NORTH_CENTRAL" | "US_WEST" | "US_SOUTH_CENTRAL" | "EUROPE_NORTH" | "EUROPE_WEST" | "US_WEST_CENTRAL" | "US_WEST_2" | "US_WEST_3" | "CANADA_EAST" | "CANADA_CENTRAL" | "BRAZIL_SOUTH" | "BRAZIL_SOUTHEAST" | "AUSTRALIA_EAST" | "AUSTRALIA_SOUTH_EAST" | "AUSTRALIA_CENTRAL" | "AUSTRALIA_CENTRAL_2" | "UAE_NORTH" | "GERMANY_WEST_CENTRAL" | "GERMANY_NORTH" | "SWITZERLAND_NORTH" | "SWITZERLAND_WEST" | "SWEDEN_CENTRAL" | "SWEDEN_SOUTH" | "UK_SOUTH" | "UK_WEST" | "INDIA_CENTRAL" | "INDIA_WEST" | "INDIA_SOUTH" | "CHINA_EAST" | "CHINA_NORTH" | "ASIA_EAST" | "JAPAN_EAST" | "JAPAN_WEST" | "ASIA_SOUTH_EAST" | "KOREA_CENTRAL" | "KOREA_SOUTH" | "FRANCE_CENTRAL" | "FRANCE_SOUTH" | "SOUTH_AFRICA_NORTH" | "SOUTH_AFRICA_WEST" | "NORWAY_EAST" | "NORWAY_WEST" | "UAE_CENTRAL" | "QATAR_CENTRAL" | "POLAND_CENTRAL" | "ISRAEL_CENTRAL" | "ITALY_NORTH" | "SPAIN_CENTRAL" | "MEXICO_CENTRAL" | "NEW_ZEALAND_NORTH"; /** @description Unique string that identifies the Azure VNet in which MongoDB Cloud clusters in this network peering container exist. The response returns **null** if no clusters exist in this network peering container. */ readonly vnetName?: string; } & { @@ -749,7 +901,7 @@ export interface components { * @description Microsoft Azure Regions. * @enum {string} */ - regionName?: "US_CENTRAL" | "US_EAST" | "US_EAST_2" | "US_NORTH_CENTRAL" | "US_WEST" | "US_SOUTH_CENTRAL" | "EUROPE_NORTH" | "EUROPE_WEST" | "US_WEST_CENTRAL" | "US_WEST_2" | "US_WEST_3" | "CANADA_EAST" | "CANADA_CENTRAL" | "BRAZIL_SOUTH" | "BRAZIL_SOUTHEAST" | "AUSTRALIA_CENTRAL" | "AUSTRALIA_CENTRAL_2" | "AUSTRALIA_EAST" | "AUSTRALIA_SOUTH_EAST" | "GERMANY_CENTRAL" | "GERMANY_NORTH_EAST" | "GERMANY_WEST_CENTRAL" | "GERMANY_NORTH" | "SWEDEN_CENTRAL" | "SWEDEN_SOUTH" | "SWITZERLAND_NORTH" | "SWITZERLAND_WEST" | "UK_SOUTH" | "UK_WEST" | "NORWAY_EAST" | "NORWAY_WEST" | "INDIA_CENTRAL" | "INDIA_SOUTH" | "INDIA_WEST" | "CHINA_EAST" | "CHINA_NORTH" | "ASIA_EAST" | "JAPAN_EAST" | "JAPAN_WEST" | "ASIA_SOUTH_EAST" | "KOREA_CENTRAL" | "KOREA_SOUTH" | "FRANCE_CENTRAL" | "FRANCE_SOUTH" | "SOUTH_AFRICA_NORTH" | "SOUTH_AFRICA_WEST" | "UAE_CENTRAL" | "UAE_NORTH" | "QATAR_CENTRAL"; + regionName?: "US_CENTRAL" | "US_EAST" | "US_EAST_2" | "US_NORTH_CENTRAL" | "US_WEST" | "US_SOUTH_CENTRAL" | "EUROPE_NORTH" | "EUROPE_WEST" | "US_WEST_CENTRAL" | "US_WEST_2" | "US_WEST_3" | "CANADA_EAST" | "CANADA_CENTRAL" | "BRAZIL_SOUTH" | "BRAZIL_SOUTHEAST" | "AUSTRALIA_CENTRAL" | "AUSTRALIA_CENTRAL_2" | "AUSTRALIA_EAST" | "AUSTRALIA_SOUTH_EAST" | "GERMANY_WEST_CENTRAL" | "GERMANY_NORTH" | "SWEDEN_CENTRAL" | "SWEDEN_SOUTH" | "SWITZERLAND_NORTH" | "SWITZERLAND_WEST" | "UK_SOUTH" | "UK_WEST" | "NORWAY_EAST" | "NORWAY_WEST" | "INDIA_CENTRAL" | "INDIA_SOUTH" | "INDIA_WEST" | "CHINA_EAST" | "CHINA_NORTH" | "ASIA_EAST" | "JAPAN_EAST" | "JAPAN_WEST" | "ASIA_SOUTH_EAST" | "KOREA_CENTRAL" | "KOREA_SOUTH" | "FRANCE_CENTRAL" | "FRANCE_SOUTH" | "SOUTH_AFRICA_NORTH" | "SOUTH_AFRICA_WEST" | "UAE_CENTRAL" | "UAE_NORTH" | "QATAR_CENTRAL"; } & { /** * @description discriminator enum property added by openapi-typescript @@ -1666,7 +1818,7 @@ export interface components { */ providerName?: "AWS" | "AZURE" | "GCP" | "TENANT"; /** @description Physical location of your MongoDB cluster nodes. The region you choose can affect network latency for clients accessing your databases. The region name is only returned in the response for single-region clusters. When MongoDB Cloud deploys a dedicated cluster, it checks if a VPC or VPC connection exists for that provider and region. If not, MongoDB Cloud creates them as part of the deployment. It assigns the VPC a Classless Inter-Domain Routing (CIDR) block. To limit a new VPC peering connection to one Classless Inter-Domain Routing (CIDR) block and region, create the connection first. Deploy the cluster after the connection starts. GCP Clusters and Multi-region clusters require one VPC peering connection for each region. MongoDB nodes can use only the peering connection that resides in the same region as the nodes to communicate with the peered VPC. */ - regionName?: ("US_GOV_WEST_1" | "US_GOV_EAST_1" | "US_EAST_1" | "US_EAST_2" | "US_WEST_1" | "US_WEST_2" | "CA_CENTRAL_1" | "EU_NORTH_1" | "EU_WEST_1" | "EU_WEST_2" | "EU_WEST_3" | "EU_CENTRAL_1" | "EU_CENTRAL_2" | "AP_EAST_1" | "AP_NORTHEAST_1" | "AP_NORTHEAST_2" | "AP_NORTHEAST_3" | "AP_SOUTHEAST_1" | "AP_SOUTHEAST_2" | "AP_SOUTHEAST_3" | "AP_SOUTHEAST_4" | "AP_SOUTH_1" | "AP_SOUTH_2" | "SA_EAST_1" | "CN_NORTH_1" | "CN_NORTHWEST_1" | "ME_SOUTH_1" | "ME_CENTRAL_1" | "AF_SOUTH_1" | "EU_SOUTH_1" | "EU_SOUTH_2" | "IL_CENTRAL_1" | "CA_WEST_1" | "AP_SOUTHEAST_5" | "AP_SOUTHEAST_7" | "MX_CENTRAL_1" | "GLOBAL") | ("US_CENTRAL" | "US_EAST" | "US_EAST_2" | "US_NORTH_CENTRAL" | "US_WEST" | "US_SOUTH_CENTRAL" | "EUROPE_NORTH" | "EUROPE_WEST" | "US_WEST_CENTRAL" | "US_WEST_2" | "US_WEST_3" | "CANADA_EAST" | "CANADA_CENTRAL" | "BRAZIL_SOUTH" | "BRAZIL_SOUTHEAST" | "AUSTRALIA_CENTRAL" | "AUSTRALIA_CENTRAL_2" | "AUSTRALIA_EAST" | "AUSTRALIA_SOUTH_EAST" | "GERMANY_CENTRAL" | "GERMANY_NORTH_EAST" | "GERMANY_WEST_CENTRAL" | "GERMANY_NORTH" | "SWEDEN_CENTRAL" | "SWEDEN_SOUTH" | "SWITZERLAND_NORTH" | "SWITZERLAND_WEST" | "UK_SOUTH" | "UK_WEST" | "NORWAY_EAST" | "NORWAY_WEST" | "INDIA_CENTRAL" | "INDIA_SOUTH" | "INDIA_WEST" | "CHINA_EAST" | "CHINA_NORTH" | "ASIA_EAST" | "JAPAN_EAST" | "JAPAN_WEST" | "ASIA_SOUTH_EAST" | "KOREA_CENTRAL" | "KOREA_SOUTH" | "FRANCE_CENTRAL" | "FRANCE_SOUTH" | "SOUTH_AFRICA_NORTH" | "SOUTH_AFRICA_WEST" | "UAE_CENTRAL" | "UAE_NORTH" | "QATAR_CENTRAL") | ("EASTERN_US" | "EASTERN_US_AW" | "US_EAST_4" | "US_EAST_4_AW" | "US_EAST_5" | "US_EAST_5_AW" | "US_WEST_2" | "US_WEST_2_AW" | "US_WEST_3" | "US_WEST_3_AW" | "US_WEST_4" | "US_WEST_4_AW" | "US_SOUTH_1" | "US_SOUTH_1_AW" | "CENTRAL_US" | "CENTRAL_US_AW" | "WESTERN_US" | "WESTERN_US_AW" | "NORTH_AMERICA_NORTHEAST_1" | "NORTH_AMERICA_NORTHEAST_2" | "NORTH_AMERICA_SOUTH_1" | "SOUTH_AMERICA_EAST_1" | "SOUTH_AMERICA_WEST_1" | "WESTERN_EUROPE" | "EUROPE_NORTH_1" | "EUROPE_WEST_2" | "EUROPE_WEST_3" | "EUROPE_WEST_4" | "EUROPE_WEST_6" | "EUROPE_WEST_8" | "EUROPE_WEST_9" | "EUROPE_WEST_10" | "EUROPE_WEST_12" | "EUROPE_SOUTHWEST_1" | "EUROPE_CENTRAL_2" | "MIDDLE_EAST_CENTRAL_1" | "MIDDLE_EAST_CENTRAL_2" | "MIDDLE_EAST_WEST_1" | "AUSTRALIA_SOUTHEAST_1" | "AUSTRALIA_SOUTHEAST_2" | "AFRICA_SOUTH_1" | "EASTERN_ASIA_PACIFIC" | "NORTHEASTERN_ASIA_PACIFIC" | "SOUTHEASTERN_ASIA_PACIFIC" | "ASIA_EAST_2" | "ASIA_NORTHEAST_2" | "ASIA_NORTHEAST_3" | "ASIA_SOUTH_1" | "ASIA_SOUTH_2" | "ASIA_SOUTHEAST_2"); + regionName?: ("US_GOV_WEST_1" | "US_GOV_EAST_1" | "US_EAST_1" | "US_EAST_2" | "US_WEST_1" | "US_WEST_2" | "CA_CENTRAL_1" | "EU_NORTH_1" | "EU_WEST_1" | "EU_WEST_2" | "EU_WEST_3" | "EU_CENTRAL_1" | "EU_CENTRAL_2" | "AP_EAST_1" | "AP_NORTHEAST_1" | "AP_NORTHEAST_2" | "AP_NORTHEAST_3" | "AP_SOUTHEAST_1" | "AP_SOUTHEAST_2" | "AP_SOUTHEAST_3" | "AP_SOUTHEAST_4" | "AP_SOUTH_1" | "AP_SOUTH_2" | "SA_EAST_1" | "CN_NORTH_1" | "CN_NORTHWEST_1" | "ME_SOUTH_1" | "ME_CENTRAL_1" | "AF_SOUTH_1" | "EU_SOUTH_1" | "EU_SOUTH_2" | "IL_CENTRAL_1" | "CA_WEST_1" | "AP_SOUTHEAST_5" | "AP_SOUTHEAST_7" | "MX_CENTRAL_1" | "GLOBAL") | ("US_CENTRAL" | "US_EAST" | "US_EAST_2" | "US_NORTH_CENTRAL" | "US_WEST" | "US_SOUTH_CENTRAL" | "EUROPE_NORTH" | "EUROPE_WEST" | "US_WEST_CENTRAL" | "US_WEST_2" | "US_WEST_3" | "CANADA_EAST" | "CANADA_CENTRAL" | "BRAZIL_SOUTH" | "BRAZIL_SOUTHEAST" | "AUSTRALIA_CENTRAL" | "AUSTRALIA_CENTRAL_2" | "AUSTRALIA_EAST" | "AUSTRALIA_SOUTH_EAST" | "GERMANY_WEST_CENTRAL" | "GERMANY_NORTH" | "SWEDEN_CENTRAL" | "SWEDEN_SOUTH" | "SWITZERLAND_NORTH" | "SWITZERLAND_WEST" | "UK_SOUTH" | "UK_WEST" | "NORWAY_EAST" | "NORWAY_WEST" | "INDIA_CENTRAL" | "INDIA_SOUTH" | "INDIA_WEST" | "CHINA_EAST" | "CHINA_NORTH" | "ASIA_EAST" | "JAPAN_EAST" | "JAPAN_WEST" | "ASIA_SOUTH_EAST" | "KOREA_CENTRAL" | "KOREA_SOUTH" | "FRANCE_CENTRAL" | "FRANCE_SOUTH" | "SOUTH_AFRICA_NORTH" | "SOUTH_AFRICA_WEST" | "UAE_CENTRAL" | "UAE_NORTH" | "QATAR_CENTRAL") | ("EASTERN_US" | "EASTERN_US_AW" | "US_EAST_4" | "US_EAST_4_AW" | "US_EAST_5" | "US_EAST_5_AW" | "US_WEST_2" | "US_WEST_2_AW" | "US_WEST_3" | "US_WEST_3_AW" | "US_WEST_4" | "US_WEST_4_AW" | "US_SOUTH_1" | "US_SOUTH_1_AW" | "CENTRAL_US" | "CENTRAL_US_AW" | "WESTERN_US" | "WESTERN_US_AW" | "NORTH_AMERICA_NORTHEAST_1" | "NORTH_AMERICA_NORTHEAST_2" | "NORTH_AMERICA_SOUTH_1" | "SOUTH_AMERICA_EAST_1" | "SOUTH_AMERICA_WEST_1" | "WESTERN_EUROPE" | "EUROPE_NORTH_1" | "EUROPE_WEST_2" | "EUROPE_WEST_3" | "EUROPE_WEST_4" | "EUROPE_WEST_6" | "EUROPE_WEST_8" | "EUROPE_WEST_9" | "EUROPE_WEST_10" | "EUROPE_WEST_12" | "EUROPE_SOUTHWEST_1" | "EUROPE_CENTRAL_2" | "MIDDLE_EAST_CENTRAL_1" | "MIDDLE_EAST_CENTRAL_2" | "MIDDLE_EAST_WEST_1" | "AUSTRALIA_SOUTHEAST_1" | "AUSTRALIA_SOUTHEAST_2" | "AFRICA_SOUTH_1" | "EASTERN_ASIA_PACIFIC" | "NORTHEASTERN_ASIA_PACIFIC" | "SOUTHEASTERN_ASIA_PACIFIC" | "ASIA_EAST_2" | "ASIA_NORTHEAST_2" | "ASIA_NORTHEAST_3" | "ASIA_SOUTH_1" | "ASIA_SOUTH_2" | "ASIA_SOUTHEAST_2"); } & (components["schemas"]["AWSRegionConfig"] | components["schemas"]["AzureRegionConfig"] | components["schemas"]["GCPRegionConfig"] | components["schemas"]["TenantRegionConfig"]); /** * Cloud Service Provider Settings @@ -1687,8 +1839,87 @@ export interface components { */ providerName?: "AWS" | "AZURE" | "GCP" | "TENANT"; /** @description Physical location of your MongoDB cluster nodes. The region you choose can affect network latency for clients accessing your databases. The region name is only returned in the response for single-region clusters. When MongoDB Cloud deploys a dedicated cluster, it checks if a VPC or VPC connection exists for that provider and region. If not, MongoDB Cloud creates them as part of the deployment. It assigns the VPC a Classless Inter-Domain Routing (CIDR) block. To limit a new VPC peering connection to one Classless Inter-Domain Routing (CIDR) block and region, create the connection first. Deploy the cluster after the connection starts. GCP Clusters and Multi-region clusters require one VPC peering connection for each region. MongoDB nodes can use only the peering connection that resides in the same region as the nodes to communicate with the peered VPC. */ - regionName?: ("US_GOV_WEST_1" | "US_GOV_EAST_1" | "US_EAST_1" | "US_EAST_2" | "US_WEST_1" | "US_WEST_2" | "CA_CENTRAL_1" | "EU_NORTH_1" | "EU_WEST_1" | "EU_WEST_2" | "EU_WEST_3" | "EU_CENTRAL_1" | "EU_CENTRAL_2" | "AP_EAST_1" | "AP_NORTHEAST_1" | "AP_NORTHEAST_2" | "AP_NORTHEAST_3" | "AP_SOUTHEAST_1" | "AP_SOUTHEAST_2" | "AP_SOUTHEAST_3" | "AP_SOUTHEAST_4" | "AP_SOUTH_1" | "AP_SOUTH_2" | "SA_EAST_1" | "CN_NORTH_1" | "CN_NORTHWEST_1" | "ME_SOUTH_1" | "ME_CENTRAL_1" | "AF_SOUTH_1" | "EU_SOUTH_1" | "EU_SOUTH_2" | "IL_CENTRAL_1" | "CA_WEST_1" | "AP_SOUTHEAST_5" | "AP_SOUTHEAST_7" | "MX_CENTRAL_1" | "GLOBAL") | ("US_CENTRAL" | "US_EAST" | "US_EAST_2" | "US_NORTH_CENTRAL" | "US_WEST" | "US_SOUTH_CENTRAL" | "EUROPE_NORTH" | "EUROPE_WEST" | "US_WEST_CENTRAL" | "US_WEST_2" | "US_WEST_3" | "CANADA_EAST" | "CANADA_CENTRAL" | "BRAZIL_SOUTH" | "BRAZIL_SOUTHEAST" | "AUSTRALIA_CENTRAL" | "AUSTRALIA_CENTRAL_2" | "AUSTRALIA_EAST" | "AUSTRALIA_SOUTH_EAST" | "GERMANY_CENTRAL" | "GERMANY_NORTH_EAST" | "GERMANY_WEST_CENTRAL" | "GERMANY_NORTH" | "SWEDEN_CENTRAL" | "SWEDEN_SOUTH" | "SWITZERLAND_NORTH" | "SWITZERLAND_WEST" | "UK_SOUTH" | "UK_WEST" | "NORWAY_EAST" | "NORWAY_WEST" | "INDIA_CENTRAL" | "INDIA_SOUTH" | "INDIA_WEST" | "CHINA_EAST" | "CHINA_NORTH" | "ASIA_EAST" | "JAPAN_EAST" | "JAPAN_WEST" | "ASIA_SOUTH_EAST" | "KOREA_CENTRAL" | "KOREA_SOUTH" | "FRANCE_CENTRAL" | "FRANCE_SOUTH" | "SOUTH_AFRICA_NORTH" | "SOUTH_AFRICA_WEST" | "UAE_CENTRAL" | "UAE_NORTH" | "QATAR_CENTRAL") | ("EASTERN_US" | "EASTERN_US_AW" | "US_EAST_4" | "US_EAST_4_AW" | "US_EAST_5" | "US_EAST_5_AW" | "US_WEST_2" | "US_WEST_2_AW" | "US_WEST_3" | "US_WEST_3_AW" | "US_WEST_4" | "US_WEST_4_AW" | "US_SOUTH_1" | "US_SOUTH_1_AW" | "CENTRAL_US" | "CENTRAL_US_AW" | "WESTERN_US" | "WESTERN_US_AW" | "NORTH_AMERICA_NORTHEAST_1" | "NORTH_AMERICA_NORTHEAST_2" | "NORTH_AMERICA_SOUTH_1" | "SOUTH_AMERICA_EAST_1" | "SOUTH_AMERICA_WEST_1" | "WESTERN_EUROPE" | "EUROPE_NORTH_1" | "EUROPE_WEST_2" | "EUROPE_WEST_3" | "EUROPE_WEST_4" | "EUROPE_WEST_6" | "EUROPE_WEST_8" | "EUROPE_WEST_9" | "EUROPE_WEST_10" | "EUROPE_WEST_12" | "EUROPE_SOUTHWEST_1" | "EUROPE_CENTRAL_2" | "MIDDLE_EAST_CENTRAL_1" | "MIDDLE_EAST_CENTRAL_2" | "MIDDLE_EAST_WEST_1" | "AUSTRALIA_SOUTHEAST_1" | "AUSTRALIA_SOUTHEAST_2" | "AFRICA_SOUTH_1" | "EASTERN_ASIA_PACIFIC" | "NORTHEASTERN_ASIA_PACIFIC" | "SOUTHEASTERN_ASIA_PACIFIC" | "ASIA_EAST_2" | "ASIA_NORTHEAST_2" | "ASIA_NORTHEAST_3" | "ASIA_SOUTH_1" | "ASIA_SOUTH_2" | "ASIA_SOUTHEAST_2"); + regionName?: ("US_GOV_WEST_1" | "US_GOV_EAST_1" | "US_EAST_1" | "US_EAST_2" | "US_WEST_1" | "US_WEST_2" | "CA_CENTRAL_1" | "EU_NORTH_1" | "EU_WEST_1" | "EU_WEST_2" | "EU_WEST_3" | "EU_CENTRAL_1" | "EU_CENTRAL_2" | "AP_EAST_1" | "AP_NORTHEAST_1" | "AP_NORTHEAST_2" | "AP_NORTHEAST_3" | "AP_SOUTHEAST_1" | "AP_SOUTHEAST_2" | "AP_SOUTHEAST_3" | "AP_SOUTHEAST_4" | "AP_SOUTH_1" | "AP_SOUTH_2" | "SA_EAST_1" | "CN_NORTH_1" | "CN_NORTHWEST_1" | "ME_SOUTH_1" | "ME_CENTRAL_1" | "AF_SOUTH_1" | "EU_SOUTH_1" | "EU_SOUTH_2" | "IL_CENTRAL_1" | "CA_WEST_1" | "AP_SOUTHEAST_5" | "AP_SOUTHEAST_7" | "MX_CENTRAL_1" | "GLOBAL") | ("US_CENTRAL" | "US_EAST" | "US_EAST_2" | "US_NORTH_CENTRAL" | "US_WEST" | "US_SOUTH_CENTRAL" | "EUROPE_NORTH" | "EUROPE_WEST" | "US_WEST_CENTRAL" | "US_WEST_2" | "US_WEST_3" | "CANADA_EAST" | "CANADA_CENTRAL" | "BRAZIL_SOUTH" | "BRAZIL_SOUTHEAST" | "AUSTRALIA_CENTRAL" | "AUSTRALIA_CENTRAL_2" | "AUSTRALIA_EAST" | "AUSTRALIA_SOUTH_EAST" | "GERMANY_WEST_CENTRAL" | "GERMANY_NORTH" | "SWEDEN_CENTRAL" | "SWEDEN_SOUTH" | "SWITZERLAND_NORTH" | "SWITZERLAND_WEST" | "UK_SOUTH" | "UK_WEST" | "NORWAY_EAST" | "NORWAY_WEST" | "INDIA_CENTRAL" | "INDIA_SOUTH" | "INDIA_WEST" | "CHINA_EAST" | "CHINA_NORTH" | "ASIA_EAST" | "JAPAN_EAST" | "JAPAN_WEST" | "ASIA_SOUTH_EAST" | "KOREA_CENTRAL" | "KOREA_SOUTH" | "FRANCE_CENTRAL" | "FRANCE_SOUTH" | "SOUTH_AFRICA_NORTH" | "SOUTH_AFRICA_WEST" | "UAE_CENTRAL" | "UAE_NORTH" | "QATAR_CENTRAL") | ("EASTERN_US" | "EASTERN_US_AW" | "US_EAST_4" | "US_EAST_4_AW" | "US_EAST_5" | "US_EAST_5_AW" | "US_WEST_2" | "US_WEST_2_AW" | "US_WEST_3" | "US_WEST_3_AW" | "US_WEST_4" | "US_WEST_4_AW" | "US_SOUTH_1" | "US_SOUTH_1_AW" | "CENTRAL_US" | "CENTRAL_US_AW" | "WESTERN_US" | "WESTERN_US_AW" | "NORTH_AMERICA_NORTHEAST_1" | "NORTH_AMERICA_NORTHEAST_2" | "NORTH_AMERICA_SOUTH_1" | "SOUTH_AMERICA_EAST_1" | "SOUTH_AMERICA_WEST_1" | "WESTERN_EUROPE" | "EUROPE_NORTH_1" | "EUROPE_WEST_2" | "EUROPE_WEST_3" | "EUROPE_WEST_4" | "EUROPE_WEST_6" | "EUROPE_WEST_8" | "EUROPE_WEST_9" | "EUROPE_WEST_10" | "EUROPE_WEST_12" | "EUROPE_SOUTHWEST_1" | "EUROPE_CENTRAL_2" | "MIDDLE_EAST_CENTRAL_1" | "MIDDLE_EAST_CENTRAL_2" | "MIDDLE_EAST_WEST_1" | "AUSTRALIA_SOUTHEAST_1" | "AUSTRALIA_SOUTHEAST_2" | "AFRICA_SOUTH_1" | "EASTERN_ASIA_PACIFIC" | "NORTHEASTERN_ASIA_PACIFIC" | "SOUTHEASTERN_ASIA_PACIFIC" | "ASIA_EAST_2" | "ASIA_NORTHEAST_2" | "ASIA_NORTHEAST_3" | "ASIA_SOUTH_1" | "ASIA_SOUTH_2" | "ASIA_SOUTHEAST_2"); } & (components["schemas"]["AWSRegionConfig20240805"] | components["schemas"]["AzureRegionConfig20240805"] | components["schemas"]["GCPRegionConfig20240805"] | components["schemas"]["TenantRegionConfig20240805"]); + /** + * Cluster Alerts + * @description Cluster alert notifies different activities and conditions about cluster of mongod hosts. + */ + ClusterAlertView: { + /** + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. + */ + acknowledgedUntil?: string; + /** + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. + */ + acknowledgementComment?: string; + /** + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. + */ + readonly acknowledgingUsername?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly alertConfigId: string; + /** + * @description Human-readable label that identifies the cluster to which this alert applies. This resource returns this parameter for alerts of events impacting backups, replica sets, or sharded clusters. + * @example cluster1 + */ + readonly clusterName?: string; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly created: string; + eventTypeName: components["schemas"]["ClusterEventTypeViewAlertable"]; + /** + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly id: string; + /** + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. + */ + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly orgId?: string; + /** + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. + */ + readonly resolved?: string; + /** + * @description State of this alert at the time you requested its details. + * @example OPEN + * @enum {string} + */ + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; + }; /** * Cluster Connection Strings * @description Collection of Uniform Resource Locators that point to the MongoDB database. @@ -1716,7 +1947,7 @@ export interface components { ClusterDescription20240805: { /** * Format: date-time - * @description If reconfiguration is necessary to regain a primary due to a regional outage, submit this field alongside your topology reconfiguration to request a new regional outage resistant topology. Forced reconfigurations during an outage of the majority of electable nodes carry a risk of data loss if replicated writes (even majority committed writes) have not been replicated to the new primary node. MongoDB Atlas docs contain more information. To proceed with an operation which carries that risk, set **acceptDataRisksAndForceReplicaSetReconfig** to the current date. + * @description If reconfiguration is necessary to regain a primary due to a regional outage, submit this field alongside your topology reconfiguration to request a new regional outage resistant topology. Forced reconfigurations during an outage of the majority of electable nodes carry a risk of data loss if replicated writes (even majority committed writes) have not been replicated to the new primary node. MongoDB Atlas docs contain more information. To proceed with an operation which carries that risk, set **acceptDataRisksAndForceReplicaSetReconfig** to the current date. This parameter expresses its value in the ISO 8601 timestamp format in UTC. */ acceptDataRisksAndForceReplicaSetReconfig?: string; advancedConfiguration?: components["schemas"]["ApiAtlasClusterAdvancedConfigurationView"]; @@ -1767,7 +1998,7 @@ export interface components { readonly featureCompatibilityVersion?: string; /** * Format: date-time - * @description Feature compatibility version expiration date. Will only appear if FCV is pinned. + * @description Feature compatibility version expiration date. Will only appear if FCV is pinned. This parameter expresses its value in the ISO 8601 timestamp format in UTC. */ readonly featureCompatibilityVersionExpirationDate?: string; /** @description Set this field to configure the Sharding Management Mode when creating a new Global Cluster. @@ -1893,6 +2124,13 @@ export interface components { /** @description Region where the private endpoint is deployed. */ readonly region?: string; }; + /** + * Cluster Event Types + * @description Event type that triggers an alert. + * @example CLUSTER_MONGOS_IS_MISSING + * @enum {string} + */ + ClusterEventTypeViewAlertable: "CLUSTER_MONGOS_IS_MISSING" | "CLUSTER_AGENT_IN_CRASH_LOOP"; ClusterFlexProviderSettings: Omit & { /** * @description Cloud service provider on which MongoDB Cloud provisioned the multi-tenant host. The resource returns this parameter when **providerSettings.providerName** is `FLEX` and **providerSetting.instanceSizeName** is `FLEX`. @@ -2420,6 +2658,120 @@ export interface components { name?: string; provider: string; } & (components["schemas"]["DataLakeS3StoreSettings"] | components["schemas"]["DataLakeDLSAWSStore"] | components["schemas"]["DataLakeDLSAzureStore"] | components["schemas"]["DataLakeDLSGCPStore"] | components["schemas"]["DataLakeAtlasStoreInstance"] | components["schemas"]["DataLakeHTTPStore"] | components["schemas"]["DataLakeAzureBlobStore"] | components["schemas"]["DataLakeGoogleCloudStorageStore"]); + DataMetricAlertView: { + /** + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. + */ + acknowledgedUntil?: string; + /** + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. + */ + acknowledgementComment?: string; + /** + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. + */ + readonly acknowledgingUsername?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly alertConfigId: string; + /** + * @description Human-readable label that identifies the cluster to which this alert applies. This resource returns this parameter for alerts of events impacting backups, replica sets, or sharded clusters. + * @example cluster1 + */ + readonly clusterName?: string; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly created: string; + currentValue?: components["schemas"]["DataMetricValueView"]; + eventTypeName: components["schemas"]["HostMetricEventTypeViewAlertable"]; + /** + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Hostname and port of the host to which this alert applies. The resource returns this parameter for alerts of events impacting hosts or replica sets. + * @example cloud-test.mongodb.com:27017 + */ + readonly hostnameAndPort?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly id: string; + /** + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. + */ + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** + * @description Name of the metric against which Atlas checks the configured `metricThreshold.threshold`. + * + * To learn more about the available metrics, see Host Metrics. + * + * **NOTE**: If you set eventTypeName to OUTSIDE_SERVERLESS_METRIC_THRESHOLD, you can specify only metrics available for serverless. To learn more, see Serverless Measurements. + * @example ASSERT_USER + */ + readonly metricName?: string; + /** + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly orgId?: string; + /** + * @description Name of the replica set to which this alert applies. The response returns this parameter for alerts of events impacting backups, hosts, or replica sets. + * @example event-replica-set + */ + readonly replicaSetName?: string; + /** + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. + */ + readonly resolved?: string; + /** + * @description State of this alert at the time you requested its details. + * @example OPEN + * @enum {string} + */ + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; + }; + /** + * Data Metric Units + * @description Element used to express the quantity. This can be an element of time, storage capacity, and the like. + * @example BYTES + * @enum {string} + */ + DataMetricUnits: "BITS" | "KILOBITS" | "MEGABITS" | "GIGABITS" | "BYTES" | "KILOBYTES" | "MEGABYTES" | "GIGABYTES" | "TERABYTES" | "PETABYTES"; + /** + * Data Metric Value + * @description Measurement of the **metricName** recorded at the time of the event. + */ + DataMetricValueView: { + /** + * Format: double + * @description Amount of the **metricName** recorded at the time of the event. This value triggered the alert. + */ + readonly number?: number; + units?: components["schemas"]["DataMetricUnits"]; + }; /** @description Settings to configure the region where you wish to store your archived data. */ DataProcessRegionView: { /** @@ -2502,58 +2854,133 @@ export interface components { */ nodeCount?: number; } & (components["schemas"]["AWSHardwareSpec20240805"] | components["schemas"]["AzureHardwareSpec20240805"] | components["schemas"]["GCPHardwareSpec20240805"]); - DefaultScheduleView: Omit, "type"> & { + /** + * Any Other Alerts + * @description Other alerts which don't have extra details beside of basic one. + */ + DefaultAlertViewForNdsGroup: { /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. */ - type: "DEFAULT"; - } & { + acknowledgedUntil?: string; /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. */ - type: "DEFAULT"; - }; - DiskBackupSnapshotAWSExportBucketRequest: Omit, "cloudProvider"> & { + acknowledgementComment?: string; /** - * @description Human-readable label that identifies the AWS S3 Bucket that the role is authorized to export to. - * @example export-bucket + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. */ - bucketName: string; + readonly acknowledgingUsername?: string; /** - * @description Unique 24-hexadecimal character string that identifies the Unified AWS Access role ID that MongoDB Cloud uses to access the AWS S3 bucket. + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. * @example 32b6e34b3d91647abb20e7b8 */ - iamRoleId: string; - } & { + readonly alertConfigId: string; /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. */ - cloudProvider: "AWS"; - }; - DiskBackupSnapshotAWSExportBucketResponse: { + readonly created: string; + /** @description Incident that triggered this alert. */ + readonly eventTypeName: ("CREDIT_CARD_ABOUT_TO_EXPIRE" | "PENDING_INVOICE_OVER_THRESHOLD" | "DAILY_BILL_OVER_THRESHOLD") | ("CPS_SNAPSHOT_STARTED" | "CPS_SNAPSHOT_SUCCESSFUL" | "CPS_SNAPSHOT_FAILED" | "CPS_CONCURRENT_SNAPSHOT_FAILED_WILL_RETRY" | "CPS_SNAPSHOT_BEHIND" | "CPS_COPY_SNAPSHOT_STARTED" | "CPS_COPY_SNAPSHOT_FAILED" | "CPS_COPY_SNAPSHOT_FAILED_WILL_RETRY" | "CPS_COPY_SNAPSHOT_SUCCESSFUL" | "CPS_PREV_SNAPSHOT_OLD" | "CPS_SNAPSHOT_FALLBACK_SUCCESSFUL" | "CPS_SNAPSHOT_FALLBACK_FAILED" | "CPS_RESTORE_SUCCESSFUL" | "CPS_EXPORT_SUCCESSFUL" | "CPS_RESTORE_FAILED" | "CPS_EXPORT_FAILED" | "CPS_AUTO_EXPORT_FAILED" | "CPS_SNAPSHOT_DOWNLOAD_REQUEST_FAILED" | "CPS_OPLOG_BEHIND" | "CPS_OPLOG_CAUGHT_UP") | ("AWS_ENCRYPTION_KEY_NEEDS_ROTATION" | "AZURE_ENCRYPTION_KEY_NEEDS_ROTATION" | "GCP_ENCRYPTION_KEY_NEEDS_ROTATION" | "AWS_ENCRYPTION_KEY_INVALID" | "AZURE_ENCRYPTION_KEY_INVALID" | "GCP_ENCRYPTION_KEY_INVALID") | ("FTS_INDEX_DELETION_FAILED" | "FTS_INDEX_BUILD_COMPLETE" | "FTS_INDEX_BUILD_FAILED" | "FTS_INDEXES_RESTORE_FAILED" | "FTS_INDEXES_SYNONYM_MAPPING_INVALID") | ("USERS_WITHOUT_MULTI_FACTOR_AUTH" | "ENCRYPTION_AT_REST_KMS_NETWORK_ACCESS_DENIED" | "ENCRYPTION_AT_REST_CONFIG_NO_LONGER_VALID") | ("CLUSTER_INSTANCE_STOP_START" | "CLUSTER_INSTANCE_RESYNC_REQUESTED" | "CLUSTER_INSTANCE_UPDATE_REQUESTED" | "SAMPLE_DATASET_LOAD_REQUESTED" | "TENANT_UPGRADE_TO_SERVERLESS_SUCCESSFUL" | "TENANT_UPGRADE_TO_SERVERLESS_FAILED" | "NETWORK_PERMISSION_ENTRY_ADDED" | "NETWORK_PERMISSION_ENTRY_REMOVED" | "NETWORK_PERMISSION_ENTRY_UPDATED") | ("MAINTENANCE_IN_ADVANCED" | "MAINTENANCE_AUTO_DEFERRED" | "MAINTENANCE_STARTED" | "MAINTENANCE_NO_LONGER_NEEDED") | ("NDS_X509_USER_AUTHENTICATION_CUSTOMER_CA_EXPIRATION_CHECK" | "NDS_X509_USER_AUTHENTICATION_CUSTOMER_CRL_EXPIRATION_CHECK" | "NDS_X509_USER_AUTHENTICATION_MANAGED_USER_CERTS_EXPIRATION_CHECK") | ("ONLINE_ARCHIVE_INSUFFICIENT_INDEXES_CHECK" | "ONLINE_ARCHIVE_MAX_CONSECUTIVE_OFFLOAD_WINDOWS_CHECK") | "OUTSIDE_SERVERLESS_METRIC_THRESHOLD" | "OUTSIDE_FLEX_METRIC_THRESHOLD" | ("JOINED_GROUP" | "REMOVED_FROM_GROUP" | "USER_ROLES_CHANGED_AUDIT") | ("TAGS_MODIFIED" | "CLUSTER_TAGS_MODIFIED" | "GROUP_TAGS_MODIFIED") | ("STREAM_PROCESSOR_STATE_IS_FAILED" | "OUTSIDE_STREAM_PROCESSOR_METRIC_THRESHOLD") | ("COMPUTE_AUTO_SCALE_INITIATED_BASE" | "COMPUTE_AUTO_SCALE_INITIATED_ANALYTICS" | "COMPUTE_AUTO_SCALE_SCALE_DOWN_FAIL_BASE" | "COMPUTE_AUTO_SCALE_SCALE_DOWN_FAIL_ANALYTICS" | "COMPUTE_AUTO_SCALE_MAX_INSTANCE_SIZE_FAIL_BASE" | "COMPUTE_AUTO_SCALE_MAX_INSTANCE_SIZE_FAIL_ANALYTICS" | "COMPUTE_AUTO_SCALE_OPLOG_FAIL_BASE" | "COMPUTE_AUTO_SCALE_OPLOG_FAIL_ANALYTICS" | "DISK_AUTO_SCALE_INITIATED" | "DISK_AUTO_SCALE_MAX_DISK_SIZE_FAIL" | "DISK_AUTO_SCALE_OPLOG_FAIL" | "PREDICTIVE_COMPUTE_AUTO_SCALE_INITIATED_BASE" | "PREDICTIVE_COMPUTE_AUTO_SCALE_MAX_INSTANCE_SIZE_FAIL_BASE" | "PREDICTIVE_COMPUTE_AUTO_SCALE_OPLOG_FAIL_BASE") | ("CPS_DATA_PROTECTION_ENABLE_REQUESTED" | "CPS_DATA_PROTECTION_ENABLED" | "CPS_DATA_PROTECTION_UPDATE_REQUESTED" | "CPS_DATA_PROTECTION_UPDATED" | "CPS_DATA_PROTECTION_DISABLE_REQUESTED" | "CPS_DATA_PROTECTION_DISABLED" | "CPS_DATA_PROTECTION_APPROVED_FOR_DISABLEMENT") | "RESOURCE_POLICY_VIOLATED"; /** - * @description Unique 24-hexadecimal character string that identifies the Export Bucket. + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. * @example 32b6e34b3d91647abb20e7b8 */ - _id: string; + readonly groupId?: string; /** - * @description The name of the AWS S3 Bucket or Azure Storage Container that Snapshots are exported to. - * @example export-bucket + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 */ - bucketName: string; + readonly id: string; /** - * @description Human-readable label that identifies the cloud provider that Snapshots will be exported to. - * @enum {string} + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. */ - cloudProvider: "AWS" | "AZURE"; + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; /** - * @description Unique 24-hexadecimal character string that identifies the Unified AWS Access role ID that MongoDB Cloud uses to access the AWS S3 bucket. + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. * @example 32b6e34b3d91647abb20e7b8 */ - iamRoleId: string; + readonly orgId?: string; + /** + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. + */ + readonly resolved?: string; + /** + * @description State of this alert at the time you requested its details. + * @example OPEN + * @enum {string} + */ + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; + }; + DefaultScheduleView: Omit, "type"> & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "DEFAULT"; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "DEFAULT"; + }; + DiskBackupSnapshotAWSExportBucketRequest: Omit, "cloudProvider"> & { + /** + * @description Human-readable label that identifies the AWS S3 Bucket that the role is authorized to export to. + * @example export-bucket + */ + bucketName: string; + /** + * @description Unique 24-hexadecimal character string that identifies the Unified AWS Access role ID that MongoDB Cloud uses to access the AWS S3 bucket. + * @example 32b6e34b3d91647abb20e7b8 + */ + iamRoleId: string; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + cloudProvider: "AWS"; + }; + DiskBackupSnapshotAWSExportBucketResponse: { + /** + * @description Unique 24-hexadecimal character string that identifies the Export Bucket. + * @example 32b6e34b3d91647abb20e7b8 + */ + _id: string; + /** + * @description The name of the AWS S3 Bucket, Azure Storage Container, or Google Cloud Storage Bucket that Snapshots are exported to. + * @example export-bucket + */ + bucketName: string; + /** + * @description Human-readable label that identifies the cloud provider that Snapshots will be exported to. + * @enum {string} + */ + cloudProvider: "AWS" | "AZURE" | "GCP"; + /** + * @description Unique 24-hexadecimal character string that identifies the Unified AWS Access role ID that MongoDB Cloud uses to access the AWS S3 bucket. + * @example 32b6e34b3d91647abb20e7b8 + */ + iamRoleId: string; /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ readonly links?: components["schemas"]["Link"][]; }; @@ -2615,7 +3042,7 @@ export interface components { * @description Human-readable label that identifies the cloud provider that Snapshots are exported to. * @enum {string} */ - cloudProvider: "AWS" | "AZURE"; + cloudProvider: "AWS" | "AZURE" | "GCP"; /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ readonly links?: components["schemas"]["Link"][]; }; @@ -2627,7 +3054,7 @@ export interface components { */ _id: string; /** - * @description The name of the AWS S3 Bucket or Azure Storage Container that Snapshots are exported to. + * @description The name of the AWS S3 Bucket, Azure Storage Container, or Google Cloud Storage Bucket that Snapshots are exported to. * @example export-bucket */ bucketName: string; @@ -2635,10 +3062,41 @@ export interface components { * @description Human-readable label that identifies the cloud provider that Snapshots will be exported to. * @enum {string} */ - cloudProvider: "AWS" | "AZURE"; + cloudProvider: "AWS" | "AZURE" | "GCP"; /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ readonly links?: components["schemas"]["Link"][]; }; + DiskBackupSnapshotGCPExportBucketRequest: Omit, "cloudProvider"> & { + /** + * @description Human-readable label that identifies the Google Cloud Storage Bucket that the role is authorized to export to. + * @example export-bucket + */ + bucketName: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the GCP Cloud Provider Access Role that MongoDB Cloud uses to access the Google Cloud Storage Bucket. + * @example 32b6e34b3d91647abb20e7b8 + */ + roleId: string; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + cloudProvider: "GCP"; + }; + DiskBackupSnapshotGCPExportBucketResponse: Omit, "cloudProvider"> & { + /** + * @description Unique 24-hexadecimal digit string that identifies the GCP Cloud Provider Access Role that MongoDB Cloud uses to access the Google Cloud Storage Bucket. + * @example 32b6e34b3d91647abb20e7b8 + */ + roleId: string; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + cloudProvider: "GCP"; + }; /** @description Setting that enables disk auto-scaling. */ DiskGBAutoScaling: { /** @description Flag that indicates whether this cluster enables disk auto-scaling. The maximum memory allowed for the selected cluster tier and the oplog size can limit storage auto-scaling. */ @@ -2648,7 +3106,7 @@ export interface components { EmployeeAccessGrantView: { /** * Format: date-time - * @description Expiration date for the employee access grant. + * @description Expiration date for the employee access grant. This parameter expresses its value in the ISO 8601 timestamp format in UTC. */ expirationTime: string; /** @@ -2666,6 +3124,147 @@ export interface components { field: string; }; Fields: Record; + /** + * Flex Backup Configuration + * @description Flex backup configuration. + */ + FlexBackupSettings20241113: { + /** + * @description Flag that indicates whether backups are performed for this flex cluster. Backup uses flex cluster backups. + * @default true + */ + readonly enabled: boolean; + }; + /** + * Flex Cluster Description + * @description Group of settings that configure a MongoDB Flex cluster. + */ + FlexClusterDescription20241113: { + backupSettings?: components["schemas"]["FlexBackupSettings20241113"]; + /** + * @description Flex cluster topology. + * @default REPLICASET + * @enum {string} + */ + readonly clusterType: "REPLICASET"; + connectionStrings?: components["schemas"]["FlexConnectionStrings20241113"]; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this instance. This parameter expresses its value in ISO 8601 format in UTC. + */ + readonly createDate?: string; + /** + * @description Unique 24-hexadecimal character string that identifies the project. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the instance. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly id?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description Version of MongoDB that the instance runs. */ + readonly mongoDBVersion?: string; + /** @description Human-readable label that identifies the instance. */ + readonly name?: string; + providerSettings: components["schemas"]["FlexProviderSettings20241113"]; + /** + * @description Human-readable label that indicates the current operating condition of this instance. + * @enum {string} + */ + readonly stateName?: "IDLE" | "CREATING" | "UPDATING" | "DELETING" | "REPAIRING"; + /** @description List that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the instance. */ + tags?: components["schemas"]["ResourceTag"][]; + /** + * @description Flag that indicates whether termination protection is enabled on the cluster. If set to `true`, MongoDB Cloud won't delete the cluster. If set to `false`, MongoDB Cloud will delete the cluster. + * @default false + */ + terminationProtectionEnabled: boolean; + /** + * @description Method by which the cluster maintains the MongoDB versions. + * @default LTS + * @enum {string} + */ + readonly versionReleaseSystem: "LTS"; + }; + /** + * Flex Cluster Description Create + * @description Settings that you can specify when you create a flex cluster. + */ + FlexClusterDescriptionCreate20241113: { + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description Human-readable label that identifies the instance. */ + name: string; + providerSettings: components["schemas"]["FlexProviderSettingsCreate20241113"]; + /** @description List that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the instance. */ + tags?: components["schemas"]["ResourceTag"][]; + /** + * @description Flag that indicates whether termination protection is enabled on the cluster. If set to `true`, MongoDB Cloud won't delete the cluster. If set to `false`, MongoDB Cloud will delete the cluster. + * @default false + */ + terminationProtectionEnabled: boolean; + }; + /** + * Flex Cluster Connection Strings + * @description Collection of Uniform Resource Locators that point to the MongoDB database. + */ + FlexConnectionStrings20241113: { + /** @description Public connection string that you can use to connect to this cluster. This connection string uses the mongodb:// protocol. */ + readonly standard?: string; + /** @description Public connection string that you can use to connect to this flex cluster. This connection string uses the `mongodb+srv://` protocol. */ + readonly standardSrv?: string; + }; + /** + * Cloud Service Provider Settings for a Flex Cluster + * @description Group of cloud provider settings that configure the provisioned MongoDB flex cluster. + */ + FlexProviderSettings20241113: { + /** + * @description Cloud service provider on which MongoDB Cloud provisioned the flex cluster. + * @enum {string} + */ + readonly backingProviderName?: "AWS" | "AZURE" | "GCP"; + /** + * Format: double + * @description Storage capacity available to the flex cluster expressed in gigabytes. + */ + readonly diskSizeGB?: number; + /** + * @description Human-readable label that identifies the provider type. + * @default FLEX + * @enum {string} + */ + readonly providerName: "FLEX"; + /** @description Human-readable label that identifies the geographic location of your MongoDB flex cluster. The region you choose can affect network latency for clients accessing your databases. For a complete list of region names, see [AWS](https://docs.atlas.mongodb.com/reference/amazon-aws/#std-label-amazon-aws), [GCP](https://docs.atlas.mongodb.com/reference/google-gcp/), and [Azure](https://docs.atlas.mongodb.com/reference/microsoft-azure/). */ + readonly regionName?: string; + }; + /** + * Cloud Service Provider Settings for a Flex Cluster + * @description Group of cloud provider settings that configure the provisioned MongoDB flex cluster. + */ + FlexProviderSettingsCreate20241113: { + /** + * @description Cloud service provider on which MongoDB Cloud provisioned the flex cluster. + * @enum {string} + */ + backingProviderName: "AWS" | "AZURE" | "GCP"; + /** + * Format: double + * @description Storage capacity available to the flex cluster expressed in gigabytes. + */ + readonly diskSizeGB?: number; + /** + * @description Human-readable label that identifies the provider type. + * @default FLEX + * @enum {string} + */ + readonly providerName: "FLEX"; + /** @description Human-readable label that identifies the geographic location of your MongoDB flex cluster. The region you choose can affect network latency for clients accessing your databases. For a complete list of region names, see [AWS](https://docs.atlas.mongodb.com/reference/amazon-aws/#std-label-amazon-aws), [GCP](https://docs.atlas.mongodb.com/reference/google-gcp/), and [Azure](https://docs.atlas.mongodb.com/reference/microsoft-azure/). */ + regionName: string; + }; /** * Tenant * @description Collection of settings that configures how a cluster might scale its cluster tier and whether the cluster can scale down. @@ -3020,103 +3619,318 @@ export interface components { diskSizeGB?: number; } & (components["schemas"]["AWSHardwareSpec20240805"] | components["schemas"]["AzureHardwareSpec20240805"] | components["schemas"]["GCPHardwareSpec20240805"] | components["schemas"]["TenantHardwareSpec20240805"]); /** - * Ingestion Destination - * @description Ingestion destination of a Data Lake Pipeline. + * Host Alerts + * @description Host alert notifies about activities on mongod host. */ - IngestionSink: { + HostAlertViewForNdsGroup: { /** - * @description Type of ingestion destination of this Data Lake Pipeline. - * @enum {string} + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. */ - readonly type?: "DLS"; - }; - /** - * Ingestion Source - * @description Ingestion Source of a Data Lake Pipeline. - */ - IngestionSource: { + acknowledgedUntil?: string; /** - * @description Type of ingestion source of this Data Lake Pipeline. - * @enum {string} + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. */ - type?: "PERIODIC_CPS" | "ON_DEMAND_CPS"; - }; - /** - * Line Item - * @description One service included in this invoice. - */ - InvoiceLineItem: { - /** @description Human-readable label that identifies the cluster that incurred the charge. */ - readonly clusterName?: string; + acknowledgementComment?: string; /** - * Format: date-time - * @description Date and time when MongoDB Cloud created this line item. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. */ - readonly created?: string; + readonly acknowledgingUsername?: string; /** - * Format: int64 - * @description Sum by which MongoDB discounted this line item. MongoDB Cloud expresses this value in cents (100ths of one US Dollar). The resource returns this parameter when a discount applies. + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 */ - readonly discountCents?: number; + readonly alertConfigId: string; + /** + * @description Human-readable label that identifies the cluster to which this alert applies. This resource returns this parameter for alerts of events impacting backups, replica sets, or sharded clusters. + * @example cluster1 + */ + readonly clusterName?: string; /** * Format: date-time - * @description Date and time when when MongoDB Cloud finished charging for this line item. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. */ - readonly endDate?: string; + readonly created: string; + eventTypeName: components["schemas"]["HostEventTypeViewForNdsGroupAlertable"]; /** - * @description Unique 24-hexadecimal digit string that identifies the project associated to this line item. + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. * @example 32b6e34b3d91647abb20e7b8 */ readonly groupId?: string; - /** @description Human-readable label that identifies the project. */ - groupName?: string; - /** @description Comment that applies to this line item. */ - readonly note?: string; /** - * Format: float - * @description Percentage by which MongoDB discounted this line item. The resource returns this parameter when a discount applies. + * @description Hostname and port of the host to which this alert applies. The resource returns this parameter for alerts of events impacting hosts or replica sets. + * @example cloud-test.mongodb.com:27017 */ - readonly percentDiscount?: number; + readonly hostnameAndPort?: string; /** - * Format: double - * @description Number of units included for the line item. These can be expressions of storage (GB), time (hours), or other units. + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 */ - readonly quantity?: number; + readonly id: string; /** - * @description Human-readable description of the service that this line item provided. This Stock Keeping Unit (SKU) could be the instance type, a support charge, advanced security, or another service. - * @enum {string} + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. */ - readonly sku?: "CLASSIC_BACKUP_OPLOG" | "CLASSIC_BACKUP_STORAGE" | "CLASSIC_BACKUP_SNAPSHOT_CREATE" | "CLASSIC_BACKUP_DAILY_MINIMUM" | "CLASSIC_BACKUP_FREE_TIER" | "CLASSIC_COUPON" | "BACKUP_STORAGE_FREE_TIER" | "BACKUP_STORAGE" | "FLEX_CONSULTING" | "CLOUD_MANAGER_CLASSIC" | "CLOUD_MANAGER_BASIC_FREE_TIER" | "CLOUD_MANAGER_BASIC" | "CLOUD_MANAGER_PREMIUM" | "CLOUD_MANAGER_FREE_TIER" | "CLOUD_MANAGER_STANDARD_FREE_TIER" | "CLOUD_MANAGER_STANDARD_ANNUAL" | "CLOUD_MANAGER_STANDARD" | "CLOUD_MANAGER_FREE_TRIAL" | "ATLAS_INSTANCE_M0" | "ATLAS_INSTANCE_M2" | "ATLAS_INSTANCE_M5" | "ATLAS_AWS_INSTANCE_M10" | "ATLAS_AWS_INSTANCE_M20" | "ATLAS_AWS_INSTANCE_M30" | "ATLAS_AWS_INSTANCE_M40" | "ATLAS_AWS_INSTANCE_M50" | "ATLAS_AWS_INSTANCE_M60" | "ATLAS_AWS_INSTANCE_M80" | "ATLAS_AWS_INSTANCE_M100" | "ATLAS_AWS_INSTANCE_M140" | "ATLAS_AWS_INSTANCE_M200" | "ATLAS_AWS_INSTANCE_M300" | "ATLAS_AWS_INSTANCE_M40_LOW_CPU" | "ATLAS_AWS_INSTANCE_M50_LOW_CPU" | "ATLAS_AWS_INSTANCE_M60_LOW_CPU" | "ATLAS_AWS_INSTANCE_M80_LOW_CPU" | "ATLAS_AWS_INSTANCE_M200_LOW_CPU" | "ATLAS_AWS_INSTANCE_M300_LOW_CPU" | "ATLAS_AWS_INSTANCE_M400_LOW_CPU" | "ATLAS_AWS_INSTANCE_M700_LOW_CPU" | "ATLAS_AWS_INSTANCE_M40_NVME" | "ATLAS_AWS_INSTANCE_M50_NVME" | "ATLAS_AWS_INSTANCE_M60_NVME" | "ATLAS_AWS_INSTANCE_M80_NVME" | "ATLAS_AWS_INSTANCE_M200_NVME" | "ATLAS_AWS_INSTANCE_M400_NVME" | "ATLAS_AWS_INSTANCE_M10_PAUSED" | "ATLAS_AWS_INSTANCE_M20_PAUSED" | "ATLAS_AWS_INSTANCE_M30_PAUSED" | "ATLAS_AWS_INSTANCE_M40_PAUSED" | "ATLAS_AWS_INSTANCE_M50_PAUSED" | "ATLAS_AWS_INSTANCE_M60_PAUSED" | "ATLAS_AWS_INSTANCE_M80_PAUSED" | "ATLAS_AWS_INSTANCE_M100_PAUSED" | "ATLAS_AWS_INSTANCE_M140_PAUSED" | "ATLAS_AWS_INSTANCE_M200_PAUSED" | "ATLAS_AWS_INSTANCE_M300_PAUSED" | "ATLAS_AWS_INSTANCE_M40_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M50_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M60_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M80_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M200_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M300_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M400_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M700_LOW_CPU_PAUSED" | "ATLAS_AWS_SEARCH_INSTANCE_S20_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S30_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S40_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S50_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S60_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S70_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S80_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S30_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S40_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S50_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S60_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S80_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S90_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S100_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S110_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S40_STORAGE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S50_STORAGE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S60_STORAGE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S80_STORAGE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S90_STORAGE_NVME" | "ATLAS_AWS_STORAGE_PROVISIONED" | "ATLAS_AWS_STORAGE_STANDARD" | "ATLAS_AWS_STORAGE_STANDARD_GP3" | "ATLAS_AWS_STORAGE_IOPS" | "ATLAS_AWS_DATA_TRANSFER_SAME_REGION" | "ATLAS_AWS_DATA_TRANSFER_DIFFERENT_REGION" | "ATLAS_AWS_DATA_TRANSFER_INTERNET" | "ATLAS_AWS_BACKUP_SNAPSHOT_STORAGE" | "ATLAS_AWS_BACKUP_DOWNLOAD_VM" | "ATLAS_AWS_BACKUP_DOWNLOAD_VM_STORAGE" | "ATLAS_AWS_BACKUP_DOWNLOAD_VM_STORAGE_IOPS" | "ATLAS_AWS_PRIVATE_ENDPOINT" | "ATLAS_AWS_PRIVATE_ENDPOINT_CAPACITY_UNITS" | "ATLAS_GCP_SEARCH_INSTANCE_S20_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S30_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S40_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S50_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S60_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S70_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S80_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S30_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S40_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S50_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S60_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S70_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S80_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S90_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S100_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S110_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S120_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S130_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S140_MEMORY_LOCALSSD" | "ATLAS_GCP_INSTANCE_M10" | "ATLAS_GCP_INSTANCE_M20" | "ATLAS_GCP_INSTANCE_M30" | "ATLAS_GCP_INSTANCE_M40" | "ATLAS_GCP_INSTANCE_M50" | "ATLAS_GCP_INSTANCE_M60" | "ATLAS_GCP_INSTANCE_M80" | "ATLAS_GCP_INSTANCE_M140" | "ATLAS_GCP_INSTANCE_M200" | "ATLAS_GCP_INSTANCE_M250" | "ATLAS_GCP_INSTANCE_M300" | "ATLAS_GCP_INSTANCE_M400" | "ATLAS_GCP_INSTANCE_M40_LOW_CPU" | "ATLAS_GCP_INSTANCE_M50_LOW_CPU" | "ATLAS_GCP_INSTANCE_M60_LOW_CPU" | "ATLAS_GCP_INSTANCE_M80_LOW_CPU" | "ATLAS_GCP_INSTANCE_M200_LOW_CPU" | "ATLAS_GCP_INSTANCE_M300_LOW_CPU" | "ATLAS_GCP_INSTANCE_M400_LOW_CPU" | "ATLAS_GCP_INSTANCE_M600_LOW_CPU" | "ATLAS_GCP_INSTANCE_M10_PAUSED" | "ATLAS_GCP_INSTANCE_M20_PAUSED" | "ATLAS_GCP_INSTANCE_M30_PAUSED" | "ATLAS_GCP_INSTANCE_M40_PAUSED" | "ATLAS_GCP_INSTANCE_M50_PAUSED" | "ATLAS_GCP_INSTANCE_M60_PAUSED" | "ATLAS_GCP_INSTANCE_M80_PAUSED" | "ATLAS_GCP_INSTANCE_M140_PAUSED" | "ATLAS_GCP_INSTANCE_M200_PAUSED" | "ATLAS_GCP_INSTANCE_M250_PAUSED" | "ATLAS_GCP_INSTANCE_M300_PAUSED" | "ATLAS_GCP_INSTANCE_M400_PAUSED" | "ATLAS_GCP_INSTANCE_M40_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M50_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M60_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M80_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M200_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M300_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M400_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M600_LOW_CPU_PAUSED" | "ATLAS_GCP_DATA_TRANSFER_INTERNET" | "ATLAS_GCP_STORAGE_SSD" | "ATLAS_GCP_DATA_TRANSFER_INTER_CONNECT" | "ATLAS_GCP_DATA_TRANSFER_INTER_ZONE" | "ATLAS_GCP_DATA_TRANSFER_INTER_REGION" | "ATLAS_GCP_DATA_TRANSFER_GOOGLE" | "ATLAS_GCP_BACKUP_SNAPSHOT_STORAGE" | "ATLAS_GCP_BACKUP_DOWNLOAD_VM" | "ATLAS_GCP_BACKUP_DOWNLOAD_VM_STORAGE" | "ATLAS_GCP_PRIVATE_ENDPOINT" | "ATLAS_GCP_PRIVATE_ENDPOINT_CAPACITY_UNITS" | "ATLAS_GCP_SNAPSHOT_COPY_DATA_TRANSFER" | "ATLAS_AZURE_INSTANCE_M10" | "ATLAS_AZURE_INSTANCE_M20" | "ATLAS_AZURE_INSTANCE_M30" | "ATLAS_AZURE_INSTANCE_M40" | "ATLAS_AZURE_INSTANCE_M50" | "ATLAS_AZURE_INSTANCE_M60" | "ATLAS_AZURE_INSTANCE_M80" | "ATLAS_AZURE_INSTANCE_M90" | "ATLAS_AZURE_INSTANCE_M200" | "ATLAS_AZURE_INSTANCE_R40" | "ATLAS_AZURE_INSTANCE_R50" | "ATLAS_AZURE_INSTANCE_R60" | "ATLAS_AZURE_INSTANCE_R80" | "ATLAS_AZURE_INSTANCE_R200" | "ATLAS_AZURE_INSTANCE_R300" | "ATLAS_AZURE_INSTANCE_R400" | "ATLAS_AZURE_INSTANCE_M60_NVME" | "ATLAS_AZURE_INSTANCE_M80_NVME" | "ATLAS_AZURE_INSTANCE_M200_NVME" | "ATLAS_AZURE_INSTANCE_M300_NVME" | "ATLAS_AZURE_INSTANCE_M400_NVME" | "ATLAS_AZURE_INSTANCE_M600_NVME" | "ATLAS_AZURE_INSTANCE_M10_PAUSED" | "ATLAS_AZURE_INSTANCE_M20_PAUSED" | "ATLAS_AZURE_INSTANCE_M30_PAUSED" | "ATLAS_AZURE_INSTANCE_M40_PAUSED" | "ATLAS_AZURE_INSTANCE_M50_PAUSED" | "ATLAS_AZURE_INSTANCE_M60_PAUSED" | "ATLAS_AZURE_INSTANCE_M80_PAUSED" | "ATLAS_AZURE_INSTANCE_M90_PAUSED" | "ATLAS_AZURE_INSTANCE_M200_PAUSED" | "ATLAS_AZURE_INSTANCE_R40_PAUSED" | "ATLAS_AZURE_INSTANCE_R50_PAUSED" | "ATLAS_AZURE_INSTANCE_R60_PAUSED" | "ATLAS_AZURE_INSTANCE_R80_PAUSED" | "ATLAS_AZURE_INSTANCE_R200_PAUSED" | "ATLAS_AZURE_INSTANCE_R300_PAUSED" | "ATLAS_AZURE_INSTANCE_R400_PAUSED" | "ATLAS_AZURE_SEARCH_INSTANCE_S20_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S30_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S40_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S50_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S60_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S70_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S80_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S40_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S50_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S60_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S80_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S90_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S100_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S110_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S130_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S135_MEMORY_LOCALSSD" | "ATLAS_AZURE_STORAGE_P2" | "ATLAS_AZURE_STORAGE_P3" | "ATLAS_AZURE_STORAGE_P4" | "ATLAS_AZURE_STORAGE_P6" | "ATLAS_AZURE_STORAGE_P10" | "ATLAS_AZURE_STORAGE_P15" | "ATLAS_AZURE_STORAGE_P20" | "ATLAS_AZURE_STORAGE_P30" | "ATLAS_AZURE_STORAGE_P40" | "ATLAS_AZURE_STORAGE_P50" | "ATLAS_AZURE_DATA_TRANSFER" | "ATLAS_AZURE_DATA_TRANSFER_REGIONAL_VNET_IN" | "ATLAS_AZURE_DATA_TRANSFER_REGIONAL_VNET_OUT" | "ATLAS_AZURE_DATA_TRANSFER_GLOBAL_VNET_IN" | "ATLAS_AZURE_DATA_TRANSFER_GLOBAL_VNET_OUT" | "ATLAS_AZURE_DATA_TRANSFER_AVAILABILITY_ZONE_IN" | "ATLAS_AZURE_DATA_TRANSFER_AVAILABILITY_ZONE_OUT" | "ATLAS_AZURE_DATA_TRANSFER_INTER_REGION_INTRA_CONTINENT" | "ATLAS_AZURE_DATA_TRANSFER_INTER_REGION_INTER_CONTINENT" | "ATLAS_AZURE_BACKUP_SNAPSHOT_STORAGE" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P2" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P3" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P4" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P6" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P10" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P15" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P20" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P30" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P40" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P50" | "ATLAS_AZURE_STANDARD_STORAGE" | "ATLAS_AZURE_EXTENDED_STANDARD_IOPS" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_EXTENDED_IOPS" | "ATLAS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE" | "ATLAS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_EXTENDED_IOPS" | "ATLAS_BI_CONNECTOR" | "ATLAS_ADVANCED_SECURITY" | "ATLAS_ENTERPRISE_AUDITING" | "ATLAS_FREE_SUPPORT" | "ATLAS_SUPPORT" | "ATLAS_NDS_BACKFILL_SUPPORT" | "STITCH_DATA_DOWNLOADED_FREE_TIER" | "STITCH_DATA_DOWNLOADED" | "STITCH_COMPUTE_FREE_TIER" | "STITCH_COMPUTE" | "CREDIT" | "MINIMUM_CHARGE" | "CHARTS_DATA_DOWNLOADED_FREE_TIER" | "CHARTS_DATA_DOWNLOADED" | "ATLAS_DATA_LAKE_AWS_DATA_RETURNED_SAME_REGION" | "ATLAS_DATA_LAKE_AWS_DATA_RETURNED_DIFFERENT_REGION" | "ATLAS_DATA_LAKE_AWS_DATA_RETURNED_INTERNET" | "ATLAS_DATA_LAKE_AWS_DATA_SCANNED" | "ATLAS_DATA_LAKE_AWS_DATA_TRANSFERRED_FROM_DIFFERENT_REGION" | "ATLAS_NDS_AWS_DATA_LAKE_STORAGE_ACCESS" | "ATLAS_NDS_AWS_DATA_LAKE_STORAGE" | "ATLAS_DATA_FEDERATION_AZURE_DATA_RETURNED_SAME_REGION" | "ATLAS_DATA_FEDERATION_AZURE_DATA_RETURNED_SAME_CONTINENT" | "ATLAS_DATA_FEDERATION_AZURE_DATA_RETURNED_DIFFERENT_CONTINENT" | "ATLAS_DATA_FEDERATION_AZURE_DATA_RETURNED_INTERNET" | "ATLAS_DATA_FEDERATION_GCP_DATA_RETURNED_SAME_REGION" | "ATLAS_DATA_FEDERATION_GCP_DATA_RETURNED_DIFFERENT_REGION" | "ATLAS_DATA_FEDERATION_GCP_DATA_RETURNED_INTERNET" | "ATLAS_DATA_FEDERATION_AZURE_DATA_SCANNED" | "ATLAS_NDS_AZURE_DATA_LAKE_STORAGE_ACCESS" | "ATLAS_NDS_AZURE_DATA_LAKE_STORAGE" | "ATLAS_DATA_FEDERATION_GCP_DATA_SCANNED" | "ATLAS_NDS_GCP_DATA_LAKE_STORAGE_ACCESS" | "ATLAS_NDS_GCP_DATA_LAKE_STORAGE" | "ATLAS_NDS_AWS_OBJECT_STORAGE_ACCESS" | "ATLAS_NDS_AWS_COMPRESSED_OBJECT_STORAGE" | "ATLAS_NDS_AZURE_OBJECT_STORAGE_ACCESS" | "ATLAS_NDS_AZURE_OBJECT_STORAGE" | "ATLAS_NDS_AZURE_COMPRESSED_OBJECT_STORAGE" | "ATLAS_NDS_GCP_OBJECT_STORAGE_ACCESS" | "ATLAS_NDS_GCP_OBJECT_STORAGE" | "ATLAS_NDS_GCP_COMPRESSED_OBJECT_STORAGE" | "ATLAS_ARCHIVE_ACCESS_PARTITION_LOCATE" | "ATLAS_NDS_AWS_PIT_RESTORE_STORAGE_FREE_TIER" | "ATLAS_NDS_AWS_PIT_RESTORE_STORAGE" | "ATLAS_NDS_GCP_PIT_RESTORE_STORAGE_FREE_TIER" | "ATLAS_NDS_GCP_PIT_RESTORE_STORAGE" | "ATLAS_NDS_AZURE_PIT_RESTORE_STORAGE_FREE_TIER" | "ATLAS_NDS_AZURE_PIT_RESTORE_STORAGE" | "ATLAS_NDS_AZURE_PRIVATE_ENDPOINT_CAPACITY_UNITS" | "ATLAS_NDS_AZURE_CMK_PRIVATE_NETWORKING" | "ATLAS_NDS_AWS_CMK_PRIVATE_NETWORKING" | "ATLAS_NDS_AWS_OBJECT_STORAGE" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_UPLOAD" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_UPLOAD" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_M40" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_M50" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_M60" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P2" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P3" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P4" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P6" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P10" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P15" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P20" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P30" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P40" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P50" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_M40" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_M50" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_M60" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_STORAGE" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_STORAGE_IOPS" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM_M40" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM_M50" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM_M60" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM_STORAGE" | "ATLAS_NDS_AWS_SERVERLESS_RPU" | "ATLAS_NDS_AWS_SERVERLESS_WPU" | "ATLAS_NDS_AWS_SERVERLESS_STORAGE" | "ATLAS_NDS_AWS_SERVERLESS_CONTINUOUS_BACKUP" | "ATLAS_NDS_AWS_SERVERLESS_BACKUP_RESTORE_VM" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER_PREVIEW" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER_REGIONAL" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER_CROSS_REGION" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER_INTERNET" | "ATLAS_NDS_GCP_SERVERLESS_RPU" | "ATLAS_NDS_GCP_SERVERLESS_WPU" | "ATLAS_NDS_GCP_SERVERLESS_STORAGE" | "ATLAS_NDS_GCP_SERVERLESS_CONTINUOUS_BACKUP" | "ATLAS_NDS_GCP_SERVERLESS_BACKUP_RESTORE_VM" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER_PREVIEW" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER_REGIONAL" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER_CROSS_REGION" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER_INTERNET" | "ATLAS_NDS_AZURE_SERVERLESS_RPU" | "ATLAS_NDS_AZURE_SERVERLESS_WPU" | "ATLAS_NDS_AZURE_SERVERLESS_STORAGE" | "ATLAS_NDS_AZURE_SERVERLESS_CONTINUOUS_BACKUP" | "ATLAS_NDS_AZURE_SERVERLESS_BACKUP_RESTORE_VM" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER_PREVIEW" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER_REGIONAL" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER_CROSS_REGION" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER_INTERNET" | "REALM_APP_REQUESTS_FREE_TIER" | "REALM_APP_REQUESTS" | "REALM_APP_COMPUTE_FREE_TIER" | "REALM_APP_COMPUTE" | "REALM_APP_SYNC_FREE_TIER" | "REALM_APP_SYNC" | "REALM_APP_DATA_TRANSFER_FREE_TIER" | "REALM_APP_DATA_TRANSFER" | "GCP_SNAPSHOT_COPY_DISK" | "ATLAS_AWS_STREAM_PROCESSING_INSTANCE_SP10" | "ATLAS_AWS_STREAM_PROCESSING_INSTANCE_SP30" | "ATLAS_AWS_STREAM_PROCESSING_INSTANCE_SP50" | "ATLAS_AZURE_STREAM_PROCESSING_INSTANCE_SP10" | "ATLAS_AZURE_STREAM_PROCESSING_INSTANCE_SP30" | "ATLAS_AZURE_STREAM_PROCESSING_INSTANCE_SP50" | "ATLAS_AWS_STREAM_PROCESSING_DATA_TRANSFER" | "ATLAS_AZURE_STREAM_PROCESSING_DATA_TRANSFER" | "ATLAS_AWS_STREAM_PROCESSING_VPC_PEERING" | "ATLAS_AZURE_STREAM_PROCESSING_PRIVATELINK" | "ATLAS_AWS_STREAM_PROCESSING_PRIVATELINK" | "ATLAS_FLEX_AWS_100_USAGE_HOURS" | "ATLAS_FLEX_AWS_200_USAGE_HOURS" | "ATLAS_FLEX_AWS_300_USAGE_HOURS" | "ATLAS_FLEX_AWS_400_USAGE_HOURS" | "ATLAS_FLEX_AWS_500_USAGE_HOURS" | "ATLAS_FLEX_AZURE_100_USAGE_HOURS" | "ATLAS_FLEX_AZURE_200_USAGE_HOURS" | "ATLAS_FLEX_AZURE_300_USAGE_HOURS" | "ATLAS_FLEX_AZURE_400_USAGE_HOURS" | "ATLAS_FLEX_AZURE_500_USAGE_HOURS" | "ATLAS_FLEX_GCP_100_USAGE_HOURS" | "ATLAS_FLEX_GCP_200_USAGE_HOURS" | "ATLAS_FLEX_GCP_300_USAGE_HOURS" | "ATLAS_FLEX_GCP_400_USAGE_HOURS" | "ATLAS_FLEX_GCP_500_USAGE_HOURS"; + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; /** - * Format: date-time - * @description Date and time when MongoDB Cloud began charging for this line item. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 */ - readonly startDate?: string; - /** @description Human-readable label that identifies the Atlas App Services application associated with this line item. */ - readonly stitchAppName?: string; - /** @description A map of key-value pairs corresponding to the tags associated with the line item resource. */ - readonly tags?: { - [key: string]: string[]; - }; + readonly orgId?: string; /** - * Format: double - * @description Lower bound for usage amount range in current SKU tier. - * - * **NOTE**: **lineItems[n].tierLowerBound** appears only if your **lineItems[n].sku** is tiered. + * @description Name of the replica set to which this alert applies. The response returns this parameter for alerts of events impacting backups, hosts, or replica sets. + * @example event-replica-set */ - readonly tierLowerBound?: number; + readonly replicaSetName?: string; /** - * Format: double - * @description Upper bound for usage amount range in current SKU tier. - * - * **NOTE**: **lineItems[n].tierUpperBound** appears only if your **lineItems[n].sku** is tiered. + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. */ - readonly tierUpperBound?: number; + readonly resolved?: string; /** - * Format: int64 - * @description Sum of the cost set for this line item. MongoDB Cloud expresses this value in cents (100ths of one US Dollar) and calculates this value as **unitPriceDollars** × **quantity** × 100. + * @description State of this alert at the time you requested its details. + * @example OPEN + * @enum {string} */ - readonly totalPriceCents?: number; + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; + }; + /** + * Host Event Types + * @description Event type that triggers an alert. + * @example HOST_DOWN + * @enum {string} + */ + HostEventTypeViewForNdsGroupAlertable: "HOST_DOWN" | "HOST_HAS_INDEX_SUGGESTIONS" | "HOST_MONGOT_CRASHING_OOM" | "HOST_MONGOT_STOP_REPLICATION" | "HOST_NOT_ENOUGH_DISK_SPACE" | "SSH_KEY_NDS_HOST_ACCESS_REQUESTED" | "SSH_KEY_NDS_HOST_ACCESS_REFRESHED" | "PUSH_BASED_LOG_EXPORT_STOPPED" | "PUSH_BASED_LOG_EXPORT_DROPPED_LOG" | "HOST_VERSION_BEHIND" | "VERSION_BEHIND" | "HOST_EXPOSED" | "HOST_SSL_CERTIFICATE_STALE" | "HOST_SECURITY_CHECKUP_NOT_MET"; + /** + * Host Metric Alerts + * @description Host Metric Alert notifies about changes of measurements or metrics for mongod host. + */ + HostMetricAlert: { + /** + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. + */ + acknowledgedUntil?: string; + /** + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. + */ + acknowledgementComment?: string; + /** + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. + */ + readonly acknowledgingUsername?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly alertConfigId: string; + /** + * @description Human-readable label that identifies the cluster to which this alert applies. This resource returns this parameter for alerts of events impacting backups, replica sets, or sharded clusters. + * @example cluster1 + */ + readonly clusterName?: string; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly created: string; + currentValue?: components["schemas"]["HostMetricValue"]; + eventTypeName: components["schemas"]["HostMetricEventTypeViewAlertable"]; + /** + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Hostname and port of the host to which this alert applies. The resource returns this parameter for alerts of events impacting hosts or replica sets. + * @example cloud-test.mongodb.com:27017 + */ + readonly hostnameAndPort?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly id: string; + /** + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. + */ + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** + * @description Name of the metric against which Atlas checks the configured `metricThreshold.threshold`. + * + * To learn more about the available metrics, see Host Metrics. + * + * **NOTE**: If you set eventTypeName to OUTSIDE_SERVERLESS_METRIC_THRESHOLD, you can specify only metrics available for serverless. To learn more, see Serverless Measurements. + * @example ASSERT_USER + */ + readonly metricName?: string; + /** + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly orgId?: string; + /** + * @description Name of the replica set to which this alert applies. The response returns this parameter for alerts of events impacting backups, hosts, or replica sets. + * @example event-replica-set + */ + readonly replicaSetName?: string; + /** + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. + */ + readonly resolved?: string; + /** + * @description State of this alert at the time you requested its details. + * @example OPEN + * @enum {string} + */ + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; + }; + /** + * Host Metric Event Types + * @description Event type that triggers an alert. + * @example OUTSIDE_METRIC_THRESHOLD + * @enum {string} + */ + HostMetricEventTypeViewAlertable: "OUTSIDE_METRIC_THRESHOLD"; + /** @description Value of the metric that triggered the alert. The resource returns this parameter for alerts of events impacting hosts. */ + HostMetricValue: { + /** + * Format: double + * @description Amount of the **metricName** recorded at the time of the event. This value triggered the alert. + */ + readonly number?: number; + /** + * @description Element used to express the quantity in **currentValue.number**. This can be an element of time, storage capacity, and the like. This metric triggered the alert. + * @enum {string} + */ + readonly units?: "bits" | "Kbits" | "Mbits" | "Gbits" | "bytes" | "KB" | "MB" | "GB" | "TB" | "PB" | "nsec" | "msec" | "sec" | "min" | "hours" | "million minutes" | "days" | "requests" | "1000 requests" | "GB seconds" | "GB hours" | "GB days" | "RPU" | "thousand RPU" | "million RPU" | "WPU" | "thousand WPU" | "million WPU" | "count" | "thousand" | "million" | "billion"; + }; + /** + * Ingestion Destination + * @description Ingestion destination of a Data Lake Pipeline. + */ + IngestionSink: { + /** + * @description Type of ingestion destination of this Data Lake Pipeline. + * @enum {string} + */ + readonly type?: "DLS"; + }; + /** + * Ingestion Source + * @description Ingestion Source of a Data Lake Pipeline. + */ + IngestionSource: { + /** + * @description Type of ingestion source of this Data Lake Pipeline. + * @enum {string} + */ + type?: "PERIODIC_CPS" | "ON_DEMAND_CPS"; + }; + /** + * Line Item + * @description One service included in this invoice. + */ + InvoiceLineItem: { + /** @description Human-readable label that identifies the cluster that incurred the charge. */ + readonly clusterName?: string; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this line item. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly created?: string; + /** + * Format: int64 + * @description Sum by which MongoDB discounted this line item. MongoDB Cloud expresses this value in cents (100ths of one US Dollar). The resource returns this parameter when a discount applies. + */ + readonly discountCents?: number; + /** + * Format: date-time + * @description Date and time when when MongoDB Cloud finished charging for this line item. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly endDate?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the project associated to this line item. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** @description Human-readable label that identifies the project. */ + groupName?: string; + /** @description Comment that applies to this line item. */ + readonly note?: string; + /** + * Format: float + * @description Percentage by which MongoDB discounted this line item. The resource returns this parameter when a discount applies. + */ + readonly percentDiscount?: number; + /** + * Format: double + * @description Number of units included for the line item. These can be expressions of storage (GB), time (hours), or other units. + */ + readonly quantity?: number; + /** + * @description Human-readable description of the service that this line item provided. This Stock Keeping Unit (SKU) could be the instance type, a support charge, advanced security, or another service. + * @enum {string} + */ + readonly sku?: "CLASSIC_BACKUP_OPLOG" | "CLASSIC_BACKUP_STORAGE" | "CLASSIC_BACKUP_SNAPSHOT_CREATE" | "CLASSIC_BACKUP_DAILY_MINIMUM" | "CLASSIC_BACKUP_FREE_TIER" | "CLASSIC_COUPON" | "BACKUP_STORAGE_FREE_TIER" | "BACKUP_STORAGE" | "FLEX_CONSULTING" | "CLOUD_MANAGER_CLASSIC" | "CLOUD_MANAGER_BASIC_FREE_TIER" | "CLOUD_MANAGER_BASIC" | "CLOUD_MANAGER_PREMIUM" | "CLOUD_MANAGER_FREE_TIER" | "CLOUD_MANAGER_STANDARD_FREE_TIER" | "CLOUD_MANAGER_STANDARD_ANNUAL" | "CLOUD_MANAGER_STANDARD" | "CLOUD_MANAGER_FREE_TRIAL" | "ATLAS_INSTANCE_M0" | "ATLAS_INSTANCE_M2" | "ATLAS_INSTANCE_M5" | "ATLAS_AWS_INSTANCE_M10" | "ATLAS_AWS_INSTANCE_M20" | "ATLAS_AWS_INSTANCE_M30" | "ATLAS_AWS_INSTANCE_M40" | "ATLAS_AWS_INSTANCE_M50" | "ATLAS_AWS_INSTANCE_M60" | "ATLAS_AWS_INSTANCE_M80" | "ATLAS_AWS_INSTANCE_M100" | "ATLAS_AWS_INSTANCE_M140" | "ATLAS_AWS_INSTANCE_M200" | "ATLAS_AWS_INSTANCE_M300" | "ATLAS_AWS_INSTANCE_M40_LOW_CPU" | "ATLAS_AWS_INSTANCE_M50_LOW_CPU" | "ATLAS_AWS_INSTANCE_M60_LOW_CPU" | "ATLAS_AWS_INSTANCE_M80_LOW_CPU" | "ATLAS_AWS_INSTANCE_M200_LOW_CPU" | "ATLAS_AWS_INSTANCE_M300_LOW_CPU" | "ATLAS_AWS_INSTANCE_M400_LOW_CPU" | "ATLAS_AWS_INSTANCE_M700_LOW_CPU" | "ATLAS_AWS_INSTANCE_M40_NVME" | "ATLAS_AWS_INSTANCE_M50_NVME" | "ATLAS_AWS_INSTANCE_M60_NVME" | "ATLAS_AWS_INSTANCE_M80_NVME" | "ATLAS_AWS_INSTANCE_M200_NVME" | "ATLAS_AWS_INSTANCE_M400_NVME" | "ATLAS_AWS_INSTANCE_M10_PAUSED" | "ATLAS_AWS_INSTANCE_M20_PAUSED" | "ATLAS_AWS_INSTANCE_M30_PAUSED" | "ATLAS_AWS_INSTANCE_M40_PAUSED" | "ATLAS_AWS_INSTANCE_M50_PAUSED" | "ATLAS_AWS_INSTANCE_M60_PAUSED" | "ATLAS_AWS_INSTANCE_M80_PAUSED" | "ATLAS_AWS_INSTANCE_M100_PAUSED" | "ATLAS_AWS_INSTANCE_M140_PAUSED" | "ATLAS_AWS_INSTANCE_M200_PAUSED" | "ATLAS_AWS_INSTANCE_M300_PAUSED" | "ATLAS_AWS_INSTANCE_M40_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M50_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M60_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M80_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M200_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M300_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M400_LOW_CPU_PAUSED" | "ATLAS_AWS_INSTANCE_M700_LOW_CPU_PAUSED" | "ATLAS_AWS_SEARCH_INSTANCE_S20_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S30_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S40_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S50_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S60_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S70_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S80_COMPUTE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S30_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S40_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S50_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S60_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S80_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S90_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S100_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S110_MEMORY_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S40_STORAGE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S50_STORAGE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S60_STORAGE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S80_STORAGE_NVME" | "ATLAS_AWS_SEARCH_INSTANCE_S90_STORAGE_NVME" | "ATLAS_AWS_STORAGE_PROVISIONED" | "ATLAS_AWS_STORAGE_STANDARD" | "ATLAS_AWS_STORAGE_STANDARD_GP3" | "ATLAS_AWS_STORAGE_IOPS" | "ATLAS_AWS_DATA_TRANSFER_SAME_REGION" | "ATLAS_AWS_DATA_TRANSFER_DIFFERENT_REGION" | "ATLAS_AWS_DATA_TRANSFER_INTERNET" | "ATLAS_AWS_BACKUP_SNAPSHOT_STORAGE" | "ATLAS_AWS_BACKUP_DOWNLOAD_VM" | "ATLAS_AWS_BACKUP_DOWNLOAD_VM_STORAGE" | "ATLAS_AWS_BACKUP_DOWNLOAD_VM_STORAGE_IOPS" | "ATLAS_AWS_PRIVATE_ENDPOINT" | "ATLAS_AWS_PRIVATE_ENDPOINT_CAPACITY_UNITS" | "ATLAS_GCP_SEARCH_INSTANCE_S20_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S30_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S40_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S50_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S60_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S70_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S80_COMPUTE_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S30_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S40_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S50_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S60_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S70_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S80_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S90_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S100_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S110_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S120_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S130_MEMORY_LOCALSSD" | "ATLAS_GCP_SEARCH_INSTANCE_S140_MEMORY_LOCALSSD" | "ATLAS_GCP_INSTANCE_M10" | "ATLAS_GCP_INSTANCE_M20" | "ATLAS_GCP_INSTANCE_M30" | "ATLAS_GCP_INSTANCE_M40" | "ATLAS_GCP_INSTANCE_M50" | "ATLAS_GCP_INSTANCE_M60" | "ATLAS_GCP_INSTANCE_M80" | "ATLAS_GCP_INSTANCE_M140" | "ATLAS_GCP_INSTANCE_M200" | "ATLAS_GCP_INSTANCE_M250" | "ATLAS_GCP_INSTANCE_M300" | "ATLAS_GCP_INSTANCE_M400" | "ATLAS_GCP_INSTANCE_M40_LOW_CPU" | "ATLAS_GCP_INSTANCE_M50_LOW_CPU" | "ATLAS_GCP_INSTANCE_M60_LOW_CPU" | "ATLAS_GCP_INSTANCE_M80_LOW_CPU" | "ATLAS_GCP_INSTANCE_M200_LOW_CPU" | "ATLAS_GCP_INSTANCE_M300_LOW_CPU" | "ATLAS_GCP_INSTANCE_M400_LOW_CPU" | "ATLAS_GCP_INSTANCE_M600_LOW_CPU" | "ATLAS_GCP_INSTANCE_M10_PAUSED" | "ATLAS_GCP_INSTANCE_M20_PAUSED" | "ATLAS_GCP_INSTANCE_M30_PAUSED" | "ATLAS_GCP_INSTANCE_M40_PAUSED" | "ATLAS_GCP_INSTANCE_M50_PAUSED" | "ATLAS_GCP_INSTANCE_M60_PAUSED" | "ATLAS_GCP_INSTANCE_M80_PAUSED" | "ATLAS_GCP_INSTANCE_M140_PAUSED" | "ATLAS_GCP_INSTANCE_M200_PAUSED" | "ATLAS_GCP_INSTANCE_M250_PAUSED" | "ATLAS_GCP_INSTANCE_M300_PAUSED" | "ATLAS_GCP_INSTANCE_M400_PAUSED" | "ATLAS_GCP_INSTANCE_M40_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M50_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M60_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M80_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M200_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M300_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M400_LOW_CPU_PAUSED" | "ATLAS_GCP_INSTANCE_M600_LOW_CPU_PAUSED" | "ATLAS_GCP_DATA_TRANSFER_INTERNET" | "ATLAS_GCP_STORAGE_SSD" | "ATLAS_GCP_DATA_TRANSFER_INTER_CONNECT" | "ATLAS_GCP_DATA_TRANSFER_INTER_ZONE" | "ATLAS_GCP_DATA_TRANSFER_INTER_REGION" | "ATLAS_GCP_DATA_TRANSFER_GOOGLE" | "ATLAS_GCP_BACKUP_SNAPSHOT_STORAGE" | "ATLAS_GCP_BACKUP_DOWNLOAD_VM" | "ATLAS_GCP_BACKUP_DOWNLOAD_VM_STORAGE" | "ATLAS_GCP_PRIVATE_ENDPOINT" | "ATLAS_GCP_PRIVATE_ENDPOINT_CAPACITY_UNITS" | "ATLAS_GCP_SNAPSHOT_COPY_DATA_TRANSFER" | "ATLAS_AZURE_INSTANCE_M10" | "ATLAS_AZURE_INSTANCE_M20" | "ATLAS_AZURE_INSTANCE_M30" | "ATLAS_AZURE_INSTANCE_M40" | "ATLAS_AZURE_INSTANCE_M50" | "ATLAS_AZURE_INSTANCE_M60" | "ATLAS_AZURE_INSTANCE_M80" | "ATLAS_AZURE_INSTANCE_M90" | "ATLAS_AZURE_INSTANCE_M200" | "ATLAS_AZURE_INSTANCE_R40" | "ATLAS_AZURE_INSTANCE_R50" | "ATLAS_AZURE_INSTANCE_R60" | "ATLAS_AZURE_INSTANCE_R80" | "ATLAS_AZURE_INSTANCE_R200" | "ATLAS_AZURE_INSTANCE_R300" | "ATLAS_AZURE_INSTANCE_R400" | "ATLAS_AZURE_INSTANCE_M60_NVME" | "ATLAS_AZURE_INSTANCE_M80_NVME" | "ATLAS_AZURE_INSTANCE_M200_NVME" | "ATLAS_AZURE_INSTANCE_M300_NVME" | "ATLAS_AZURE_INSTANCE_M400_NVME" | "ATLAS_AZURE_INSTANCE_M600_NVME" | "ATLAS_AZURE_INSTANCE_M10_PAUSED" | "ATLAS_AZURE_INSTANCE_M20_PAUSED" | "ATLAS_AZURE_INSTANCE_M30_PAUSED" | "ATLAS_AZURE_INSTANCE_M40_PAUSED" | "ATLAS_AZURE_INSTANCE_M50_PAUSED" | "ATLAS_AZURE_INSTANCE_M60_PAUSED" | "ATLAS_AZURE_INSTANCE_M80_PAUSED" | "ATLAS_AZURE_INSTANCE_M90_PAUSED" | "ATLAS_AZURE_INSTANCE_M200_PAUSED" | "ATLAS_AZURE_INSTANCE_R40_PAUSED" | "ATLAS_AZURE_INSTANCE_R50_PAUSED" | "ATLAS_AZURE_INSTANCE_R60_PAUSED" | "ATLAS_AZURE_INSTANCE_R80_PAUSED" | "ATLAS_AZURE_INSTANCE_R200_PAUSED" | "ATLAS_AZURE_INSTANCE_R300_PAUSED" | "ATLAS_AZURE_INSTANCE_R400_PAUSED" | "ATLAS_AZURE_SEARCH_INSTANCE_S20_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S30_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S40_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S50_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S60_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S70_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S80_COMPUTE_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S40_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S50_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S60_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S80_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S90_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S100_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S110_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S130_MEMORY_LOCALSSD" | "ATLAS_AZURE_SEARCH_INSTANCE_S135_MEMORY_LOCALSSD" | "ATLAS_AZURE_STORAGE_P2" | "ATLAS_AZURE_STORAGE_P3" | "ATLAS_AZURE_STORAGE_P4" | "ATLAS_AZURE_STORAGE_P6" | "ATLAS_AZURE_STORAGE_P10" | "ATLAS_AZURE_STORAGE_P15" | "ATLAS_AZURE_STORAGE_P20" | "ATLAS_AZURE_STORAGE_P30" | "ATLAS_AZURE_STORAGE_P40" | "ATLAS_AZURE_STORAGE_P50" | "ATLAS_AZURE_DATA_TRANSFER" | "ATLAS_AZURE_DATA_TRANSFER_REGIONAL_VNET_IN" | "ATLAS_AZURE_DATA_TRANSFER_REGIONAL_VNET_OUT" | "ATLAS_AZURE_DATA_TRANSFER_GLOBAL_VNET_IN" | "ATLAS_AZURE_DATA_TRANSFER_GLOBAL_VNET_OUT" | "ATLAS_AZURE_DATA_TRANSFER_AVAILABILITY_ZONE_IN" | "ATLAS_AZURE_DATA_TRANSFER_AVAILABILITY_ZONE_OUT" | "ATLAS_AZURE_DATA_TRANSFER_INTER_REGION_INTRA_CONTINENT" | "ATLAS_AZURE_DATA_TRANSFER_INTER_REGION_INTER_CONTINENT" | "ATLAS_AZURE_BACKUP_SNAPSHOT_STORAGE" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P2" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P3" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P4" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P6" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P10" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P15" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P20" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P30" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P40" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_P50" | "ATLAS_AZURE_STANDARD_STORAGE" | "ATLAS_AZURE_EXTENDED_STANDARD_IOPS" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE" | "ATLAS_AZURE_BACKUP_DOWNLOAD_VM_STORAGE_EXTENDED_IOPS" | "ATLAS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE" | "ATLAS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_EXTENDED_IOPS" | "ATLAS_BI_CONNECTOR" | "ATLAS_ADVANCED_SECURITY" | "ATLAS_ENTERPRISE_AUDITING" | "ATLAS_FREE_SUPPORT" | "ATLAS_SUPPORT" | "ATLAS_NDS_BACKFILL_SUPPORT" | "STITCH_DATA_DOWNLOADED_FREE_TIER" | "STITCH_DATA_DOWNLOADED" | "STITCH_COMPUTE_FREE_TIER" | "STITCH_COMPUTE" | "CREDIT" | "MINIMUM_CHARGE" | "CHARTS_DATA_DOWNLOADED_FREE_TIER" | "CHARTS_DATA_DOWNLOADED" | "ATLAS_DATA_LAKE_AWS_DATA_RETURNED_SAME_REGION" | "ATLAS_DATA_LAKE_AWS_DATA_RETURNED_DIFFERENT_REGION" | "ATLAS_DATA_LAKE_AWS_DATA_RETURNED_INTERNET" | "ATLAS_DATA_LAKE_AWS_DATA_SCANNED" | "ATLAS_DATA_LAKE_AWS_DATA_TRANSFERRED_FROM_DIFFERENT_REGION" | "ATLAS_NDS_AWS_DATA_LAKE_STORAGE_ACCESS" | "ATLAS_NDS_AWS_DATA_LAKE_STORAGE" | "ATLAS_DATA_FEDERATION_AZURE_DATA_RETURNED_SAME_REGION" | "ATLAS_DATA_FEDERATION_AZURE_DATA_RETURNED_SAME_CONTINENT" | "ATLAS_DATA_FEDERATION_AZURE_DATA_RETURNED_DIFFERENT_CONTINENT" | "ATLAS_DATA_FEDERATION_AZURE_DATA_RETURNED_INTERNET" | "ATLAS_DATA_FEDERATION_GCP_DATA_RETURNED_SAME_REGION" | "ATLAS_DATA_FEDERATION_GCP_DATA_RETURNED_DIFFERENT_REGION" | "ATLAS_DATA_FEDERATION_GCP_DATA_RETURNED_INTERNET" | "ATLAS_DATA_FEDERATION_AZURE_DATA_SCANNED" | "ATLAS_NDS_AZURE_DATA_LAKE_STORAGE_ACCESS" | "ATLAS_NDS_AZURE_DATA_LAKE_STORAGE" | "ATLAS_DATA_FEDERATION_GCP_DATA_SCANNED" | "ATLAS_NDS_GCP_DATA_LAKE_STORAGE_ACCESS" | "ATLAS_NDS_GCP_DATA_LAKE_STORAGE" | "ATLAS_NDS_AWS_OBJECT_STORAGE_ACCESS" | "ATLAS_NDS_AWS_COMPRESSED_OBJECT_STORAGE" | "ATLAS_NDS_AZURE_OBJECT_STORAGE_ACCESS" | "ATLAS_NDS_AZURE_OBJECT_STORAGE" | "ATLAS_NDS_AZURE_COMPRESSED_OBJECT_STORAGE" | "ATLAS_NDS_GCP_OBJECT_STORAGE_ACCESS" | "ATLAS_NDS_GCP_OBJECT_STORAGE" | "ATLAS_NDS_GCP_COMPRESSED_OBJECT_STORAGE" | "ATLAS_ARCHIVE_ACCESS_PARTITION_LOCATE" | "ATLAS_NDS_AWS_PIT_RESTORE_STORAGE_FREE_TIER" | "ATLAS_NDS_AWS_PIT_RESTORE_STORAGE" | "ATLAS_NDS_GCP_PIT_RESTORE_STORAGE_FREE_TIER" | "ATLAS_NDS_GCP_PIT_RESTORE_STORAGE" | "ATLAS_NDS_AZURE_PIT_RESTORE_STORAGE_FREE_TIER" | "ATLAS_NDS_AZURE_PIT_RESTORE_STORAGE" | "ATLAS_NDS_AZURE_PRIVATE_ENDPOINT_CAPACITY_UNITS" | "ATLAS_NDS_AZURE_CMK_PRIVATE_NETWORKING" | "ATLAS_NDS_AWS_CMK_PRIVATE_NETWORKING" | "ATLAS_NDS_AWS_OBJECT_STORAGE" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_UPLOAD" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_UPLOAD" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_M40" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_M50" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_M60" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P2" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P3" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P4" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P6" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P10" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P15" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P20" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P30" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P40" | "ATLAS_NDS_AZURE_SNAPSHOT_EXPORT_VM_STORAGE_P50" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_M40" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_M50" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_M60" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_STORAGE" | "ATLAS_NDS_AWS_SNAPSHOT_EXPORT_VM_STORAGE_IOPS" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM_M40" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM_M50" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM_M60" | "ATLAS_NDS_GCP_SNAPSHOT_EXPORT_VM_STORAGE" | "ATLAS_NDS_AWS_SERVERLESS_RPU" | "ATLAS_NDS_AWS_SERVERLESS_WPU" | "ATLAS_NDS_AWS_SERVERLESS_STORAGE" | "ATLAS_NDS_AWS_SERVERLESS_CONTINUOUS_BACKUP" | "ATLAS_NDS_AWS_SERVERLESS_BACKUP_RESTORE_VM" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER_PREVIEW" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER_REGIONAL" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER_CROSS_REGION" | "ATLAS_NDS_AWS_SERVERLESS_DATA_TRANSFER_INTERNET" | "ATLAS_NDS_GCP_SERVERLESS_RPU" | "ATLAS_NDS_GCP_SERVERLESS_WPU" | "ATLAS_NDS_GCP_SERVERLESS_STORAGE" | "ATLAS_NDS_GCP_SERVERLESS_CONTINUOUS_BACKUP" | "ATLAS_NDS_GCP_SERVERLESS_BACKUP_RESTORE_VM" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER_PREVIEW" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER_REGIONAL" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER_CROSS_REGION" | "ATLAS_NDS_GCP_SERVERLESS_DATA_TRANSFER_INTERNET" | "ATLAS_NDS_AZURE_SERVERLESS_RPU" | "ATLAS_NDS_AZURE_SERVERLESS_WPU" | "ATLAS_NDS_AZURE_SERVERLESS_STORAGE" | "ATLAS_NDS_AZURE_SERVERLESS_CONTINUOUS_BACKUP" | "ATLAS_NDS_AZURE_SERVERLESS_BACKUP_RESTORE_VM" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER_PREVIEW" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER_REGIONAL" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER_CROSS_REGION" | "ATLAS_NDS_AZURE_SERVERLESS_DATA_TRANSFER_INTERNET" | "REALM_APP_REQUESTS_FREE_TIER" | "REALM_APP_REQUESTS" | "REALM_APP_COMPUTE_FREE_TIER" | "REALM_APP_COMPUTE" | "REALM_APP_SYNC_FREE_TIER" | "REALM_APP_SYNC" | "REALM_APP_DATA_TRANSFER_FREE_TIER" | "REALM_APP_DATA_TRANSFER" | "GCP_SNAPSHOT_COPY_DISK" | "ATLAS_AWS_STREAM_PROCESSING_INSTANCE_SP10" | "ATLAS_AWS_STREAM_PROCESSING_INSTANCE_SP30" | "ATLAS_AWS_STREAM_PROCESSING_INSTANCE_SP50" | "ATLAS_AZURE_STREAM_PROCESSING_INSTANCE_SP10" | "ATLAS_AZURE_STREAM_PROCESSING_INSTANCE_SP30" | "ATLAS_AZURE_STREAM_PROCESSING_INSTANCE_SP50" | "ATLAS_AWS_STREAM_PROCESSING_DATA_TRANSFER" | "ATLAS_AZURE_STREAM_PROCESSING_DATA_TRANSFER" | "ATLAS_AWS_STREAM_PROCESSING_VPC_PEERING" | "ATLAS_AZURE_STREAM_PROCESSING_PRIVATELINK" | "ATLAS_AWS_STREAM_PROCESSING_PRIVATELINK" | "ATLAS_FLEX_AWS_100_USAGE_HOURS" | "ATLAS_FLEX_AWS_200_USAGE_HOURS" | "ATLAS_FLEX_AWS_300_USAGE_HOURS" | "ATLAS_FLEX_AWS_400_USAGE_HOURS" | "ATLAS_FLEX_AWS_500_USAGE_HOURS" | "ATLAS_FLEX_AZURE_100_USAGE_HOURS" | "ATLAS_FLEX_AZURE_200_USAGE_HOURS" | "ATLAS_FLEX_AZURE_300_USAGE_HOURS" | "ATLAS_FLEX_AZURE_400_USAGE_HOURS" | "ATLAS_FLEX_AZURE_500_USAGE_HOURS" | "ATLAS_FLEX_GCP_100_USAGE_HOURS" | "ATLAS_FLEX_GCP_200_USAGE_HOURS" | "ATLAS_FLEX_GCP_300_USAGE_HOURS" | "ATLAS_FLEX_GCP_400_USAGE_HOURS" | "ATLAS_FLEX_GCP_500_USAGE_HOURS" | "ATLAS_FLEX_AWS_LEGACY_100_USAGE_HOURS" | "ATLAS_FLEX_AWS_LEGACY_200_USAGE_HOURS" | "ATLAS_FLEX_AWS_LEGACY_300_USAGE_HOURS" | "ATLAS_FLEX_AWS_LEGACY_400_USAGE_HOURS" | "ATLAS_FLEX_AWS_LEGACY_500_USAGE_HOURS" | "ATLAS_FLEX_AZURE_LEGACY_100_USAGE_HOURS" | "ATLAS_FLEX_AZURE_LEGACY_200_USAGE_HOURS" | "ATLAS_FLEX_AZURE_LEGACY_300_USAGE_HOURS" | "ATLAS_FLEX_AZURE_LEGACY_400_USAGE_HOURS" | "ATLAS_FLEX_AZURE_LEGACY_500_USAGE_HOURS" | "ATLAS_FLEX_GCP_LEGACY_100_USAGE_HOURS" | "ATLAS_FLEX_GCP_LEGACY_200_USAGE_HOURS" | "ATLAS_FLEX_GCP_LEGACY_300_USAGE_HOURS" | "ATLAS_FLEX_GCP_LEGACY_400_USAGE_HOURS" | "ATLAS_FLEX_GCP_LEGACY_500_USAGE_HOURS"; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud began charging for this line item. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly startDate?: string; + /** @description Human-readable label that identifies the Atlas App Services application associated with this line item. */ + readonly stitchAppName?: string; + /** @description A map of key-value pairs corresponding to the tags associated with the line item resource. */ + readonly tags?: { + [key: string]: string[]; + }; + /** + * Format: double + * @description Lower bound for usage amount range in current SKU tier. + * + * **NOTE**: **lineItems[n].tierLowerBound** appears only if your **lineItems[n].sku** is tiered. + */ + readonly tierLowerBound?: number; + /** + * Format: double + * @description Upper bound for usage amount range in current SKU tier. + * + * **NOTE**: **lineItems[n].tierUpperBound** appears only if your **lineItems[n].sku** is tiered. + */ + readonly tierUpperBound?: number; + /** + * Format: int64 + * @description Sum of the cost set for this line item. MongoDB Cloud expresses this value in cents (100ths of one US Dollar) and calculates this value as **unitPriceDollars** × **quantity** × 100. + */ + readonly totalPriceCents?: number; /** @description Element used to express what **quantity** this line item measures. This value can be elements of time, storage capacity, and the like. */ readonly unit?: string; /** @@ -3198,6 +4012,120 @@ export interface components { /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ readonly links?: components["schemas"]["Link"][]; }; + NumberMetricAlertView: { + /** + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. + */ + acknowledgedUntil?: string; + /** + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. + */ + acknowledgementComment?: string; + /** + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. + */ + readonly acknowledgingUsername?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly alertConfigId: string; + /** + * @description Human-readable label that identifies the cluster to which this alert applies. This resource returns this parameter for alerts of events impacting backups, replica sets, or sharded clusters. + * @example cluster1 + */ + readonly clusterName?: string; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly created: string; + currentValue?: components["schemas"]["NumberMetricValueView"]; + eventTypeName: components["schemas"]["HostMetricEventTypeViewAlertable"]; + /** + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Hostname and port of the host to which this alert applies. The resource returns this parameter for alerts of events impacting hosts or replica sets. + * @example cloud-test.mongodb.com:27017 + */ + readonly hostnameAndPort?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly id: string; + /** + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. + */ + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** + * @description Name of the metric against which Atlas checks the configured `metricThreshold.threshold`. + * + * To learn more about the available metrics, see Host Metrics. + * + * **NOTE**: If you set eventTypeName to OUTSIDE_SERVERLESS_METRIC_THRESHOLD, you can specify only metrics available for serverless. To learn more, see Serverless Measurements. + * @example ASSERT_USER + */ + readonly metricName?: string; + /** + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly orgId?: string; + /** + * @description Name of the replica set to which this alert applies. The response returns this parameter for alerts of events impacting backups, hosts, or replica sets. + * @example event-replica-set + */ + readonly replicaSetName?: string; + /** + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. + */ + readonly resolved?: string; + /** + * @description State of this alert at the time you requested its details. + * @example OPEN + * @enum {string} + */ + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; + }; + /** + * Number Metric Units + * @description Element used to express the quantity. This can be an element of time, storage capacity, and the like. + * @example COUNT + * @enum {string} + */ + NumberMetricUnits: "COUNT" | "THOUSAND" | "MILLION" | "BILLION"; + /** + * Number Metric Value + * @description Measurement of the **metricName** recorded at the time of the event. + */ + NumberMetricValueView: { + /** + * Format: double + * @description Amount of the **metricName** recorded at the time of the event. This value triggered the alert. + */ + readonly number?: number; + units?: components["schemas"]["NumberMetricUnits"]; + }; /** * On-Demand Cloud Provider Snapshot Source * @description On-Demand Cloud Provider Snapshots as Source for a Data Lake Pipeline. @@ -3333,113 +4261,352 @@ export interface components { /** @description List of unique 24-hexadecimal digit strings that identifies the teams to which this MongoDB Cloud user belongs. */ readonly teamIds?: string[]; /** - * Format: email - * @description Email address that represents the username of the MongoDB Cloud user. + * Format: email + * @description Email address that represents the username of the MongoDB Cloud user. + */ + readonly username: string; + } & (components["schemas"]["OrgPendingUserResponse"] | components["schemas"]["OrgActiveUserResponse"]); + /** @description Organization- and project-level roles assigned to one MongoDB Cloud user within one organization. */ + OrgUserRolesResponse: { + /** @description List of project-level role assignments assigned to the MongoDB Cloud user. */ + groupRoleAssignments?: components["schemas"]["GroupRoleAssignment"][]; + /** @description One or more organization-level roles assigned to the MongoDB Cloud user. */ + orgRoles?: ("ORG_OWNER" | "ORG_GROUP_CREATOR" | "ORG_BILLING_ADMIN" | "ORG_BILLING_READ_ONLY" | "ORG_READ_ONLY" | "ORG_MEMBER")[]; + }; + PaginatedAlertView: { + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ + readonly results?: components["schemas"]["AlertViewForNdsGroup"][]; + /** + * Format: int32 + * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + */ + readonly totalCount?: number; + }; + /** @description List of MongoDB Database users granted access to databases in the specified project. */ + PaginatedApiAtlasDatabaseUserView: { + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ + readonly results?: components["schemas"]["CloudDatabaseUser"][]; + /** + * Format: int32 + * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + */ + readonly totalCount?: number; + }; + PaginatedAtlasGroupView: { + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ + readonly results?: components["schemas"]["Group"][]; + /** + * Format: int32 + * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + */ + readonly totalCount?: number; + }; + PaginatedClusterDescription20240805: { + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ + readonly results?: components["schemas"]["ClusterDescription20240805"][]; + /** + * Format: int32 + * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + */ + readonly totalCount?: number; + }; + PaginatedFlexClusters20241113: { + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ + readonly results?: components["schemas"]["FlexClusterDescription20241113"][]; + /** + * Format: int32 + * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + */ + readonly totalCount?: number; + }; + PaginatedNetworkAccessView: { + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ + readonly results?: components["schemas"]["NetworkPermissionEntry"][]; + /** + * Format: int32 + * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + */ + readonly totalCount?: number; + }; + PaginatedOrgGroupView: { + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ + readonly results?: components["schemas"]["OrgGroup"][]; + /** + * Format: int32 + * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + */ + readonly totalCount?: number; + }; + PaginatedOrganizationView: { + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ + readonly results?: components["schemas"]["AtlasOrganization"][]; + /** + * Format: int32 + * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + */ + readonly totalCount?: number; + }; + /** + * Periodic Cloud Provider Snapshot Source + * @description Scheduled Cloud Provider Snapshot as Source for a Data Lake Pipeline. + */ + PeriodicCpsSnapshotSource: Omit & { + /** @description Human-readable name that identifies the cluster. */ + clusterName?: string; + /** @description Human-readable name that identifies the collection. */ + collectionName?: string; + /** @description Human-readable name that identifies the database. */ + databaseName?: string; + /** + * @description Unique 24-hexadecimal character string that identifies the project. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Unique 24-hexadecimal character string that identifies a policy item. + * @example 32b6e34b3d91647abb20e7b8 + */ + policyItemId?: string; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "PERIODIC_CPS"; + }; + RawMetricAlertView: { + /** + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. + */ + acknowledgedUntil?: string; + /** + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. + */ + acknowledgementComment?: string; + /** + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. + */ + readonly acknowledgingUsername?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly alertConfigId: string; + /** + * @description Human-readable label that identifies the cluster to which this alert applies. This resource returns this parameter for alerts of events impacting backups, replica sets, or sharded clusters. + * @example cluster1 + */ + readonly clusterName?: string; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly created: string; + currentValue?: components["schemas"]["RawMetricValueView"]; + eventTypeName: components["schemas"]["HostMetricEventTypeViewAlertable"]; + /** + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Hostname and port of the host to which this alert applies. The resource returns this parameter for alerts of events impacting hosts or replica sets. + * @example cloud-test.mongodb.com:27017 + */ + readonly hostnameAndPort?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly id: string; + /** + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. + */ + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** + * @description Name of the metric against which Atlas checks the configured `metricThreshold.threshold`. + * + * To learn more about the available metrics, see Host Metrics. + * + * **NOTE**: If you set eventTypeName to OUTSIDE_SERVERLESS_METRIC_THRESHOLD, you can specify only metrics available for serverless. To learn more, see Serverless Measurements. + * @example ASSERT_USER + */ + readonly metricName?: string; + /** + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly orgId?: string; + /** + * @description Name of the replica set to which this alert applies. The response returns this parameter for alerts of events impacting backups, hosts, or replica sets. + * @example event-replica-set + */ + readonly replicaSetName?: string; + /** + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. + */ + readonly resolved?: string; + /** + * @description State of this alert at the time you requested its details. + * @example OPEN + * @enum {string} + */ + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; + }; + /** + * Raw Metric Units + * @description Element used to express the quantity. This can be an element of time, storage capacity, and the like. + * @default RAW + * @enum {string} + */ + RawMetricUnits: "RAW"; + /** + * Raw Metric Value + * @description Measurement of the **metricName** recorded at the time of the event. + */ + RawMetricValueView: { + /** + * Format: double + * @description Amount of the **metricName** recorded at the time of the event. This value triggered the alert. + */ + readonly number?: number; + units?: components["schemas"]["RawMetricUnits"]; + }; + /** + * ReplicaSet Alerts + * @description ReplicaSet alert notifies about different activities on replica set of mongod instances. + */ + ReplicaSetAlertViewForNdsGroup: { + /** + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. + */ + acknowledgedUntil?: string; + /** + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. + */ + acknowledgementComment?: string; + /** + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. + */ + readonly acknowledgingUsername?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly alertConfigId: string; + /** + * @description Human-readable label that identifies the cluster to which this alert applies. This resource returns this parameter for alerts of events impacting backups, replica sets, or sharded clusters. + * @example cluster1 */ - readonly username: string; - } & (components["schemas"]["OrgPendingUserResponse"] | components["schemas"]["OrgActiveUserResponse"]); - /** @description Organization- and project-level roles assigned to one MongoDB Cloud user within one organization. */ - OrgUserRolesResponse: { - /** @description List of project-level role assignments assigned to the MongoDB Cloud user. */ - groupRoleAssignments?: components["schemas"]["GroupRoleAssignment"][]; - /** @description One or more organization-level roles assigned to the MongoDB Cloud user. */ - orgRoles?: ("ORG_OWNER" | "ORG_GROUP_CREATOR" | "ORG_BILLING_ADMIN" | "ORG_BILLING_READ_ONLY" | "ORG_READ_ONLY" | "ORG_MEMBER")[]; - }; - /** @description List of MongoDB Database users granted access to databases in the specified project. */ - PaginatedApiAtlasDatabaseUserView: { - /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ - readonly links?: components["schemas"]["Link"][]; - /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ - readonly results?: components["schemas"]["CloudDatabaseUser"][]; + readonly clusterName?: string; /** - * Format: int32 - * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. */ - readonly totalCount?: number; - }; - PaginatedAtlasGroupView: { - /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ - readonly links?: components["schemas"]["Link"][]; - /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ - readonly results?: components["schemas"]["Group"][]; + readonly created: string; + eventTypeName: components["schemas"]["ReplicaSetEventTypeViewForNdsGroupAlertable"]; /** - * Format: int32 - * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. + * @example 32b6e34b3d91647abb20e7b8 */ - readonly totalCount?: number; - }; - PaginatedClusterDescription20240805: { - /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ - readonly links?: components["schemas"]["Link"][]; - /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ - readonly results?: components["schemas"]["ClusterDescription20240805"][]; + readonly groupId?: string; /** - * Format: int32 - * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + * @description Hostname and port of the host to which this alert applies. The resource returns this parameter for alerts of events impacting hosts or replica sets. + * @example cloud-test.mongodb.com:27017 */ - readonly totalCount?: number; - }; - PaginatedNetworkAccessView: { - /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ - readonly links?: components["schemas"]["Link"][]; - /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ - readonly results?: components["schemas"]["NetworkPermissionEntry"][]; + readonly hostnameAndPort?: string; /** - * Format: int32 - * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 */ - readonly totalCount?: number; - }; - PaginatedOrgGroupView: { - /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ - readonly links?: components["schemas"]["Link"][]; - /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ - readonly results?: components["schemas"]["OrgGroup"][]; + readonly id: string; /** - * Format: int32 - * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. */ - readonly totalCount?: number; - }; - PaginatedOrganizationView: { + readonly lastNotified?: string; /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ readonly links?: components["schemas"]["Link"][]; - /** @description List of returned documents that MongoDB Cloud provides when completing this request. */ - readonly results?: components["schemas"]["AtlasOrganization"][]; + /** @description List of unique 24-hexadecimal character strings that identify the replica set members that are not in PRIMARY nor SECONDARY state. */ + readonly nonRunningHostIds?: string[]; /** - * Format: int32 - * @description Total number of documents available. MongoDB Cloud omits this value if `includeCount` is set to `false`. The total number is an estimate and may not be exact. + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 */ - readonly totalCount?: number; - }; - /** - * Periodic Cloud Provider Snapshot Source - * @description Scheduled Cloud Provider Snapshot as Source for a Data Lake Pipeline. - */ - PeriodicCpsSnapshotSource: Omit & { - /** @description Human-readable name that identifies the cluster. */ - clusterName?: string; - /** @description Human-readable name that identifies the collection. */ - collectionName?: string; - /** @description Human-readable name that identifies the database. */ - databaseName?: string; + readonly orgId?: string; /** - * @description Unique 24-hexadecimal character string that identifies the project. + * @description Unique 24-hexadecimal character string that identifies the parent cluster to which this alert applies. The parent cluster contains the sharded nodes. MongoDB Cloud returns this parameter only for alerts of events impacting sharded clusters. * @example 32b6e34b3d91647abb20e7b8 */ - readonly groupId?: string; + readonly parentClusterId?: string; /** - * @description Unique 24-hexadecimal character string that identifies a policy item. - * @example 32b6e34b3d91647abb20e7b8 + * @description Name of the replica set to which this alert applies. The response returns this parameter for alerts of events impacting backups, hosts, or replica sets. + * @example event-replica-set */ - policyItemId?: string; - } & { + readonly replicaSetName?: string; /** - * @description discriminator enum property added by openapi-typescript + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. + */ + readonly resolved?: string; + /** + * @description State of this alert at the time you requested its details. + * @example OPEN * @enum {string} */ - type: "PERIODIC_CPS"; + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; }; + /** + * ReplicaSet Event Types + * @description Incident that triggered this alert. + * @example NO_PRIMARY + * @enum {string} + */ + ReplicaSetEventTypeViewForNdsGroupAlertable: "REPLICATION_OPLOG_WINDOW_RUNNING_OUT" | "NO_PRIMARY" | "PRIMARY_ELECTED" | "TOO_MANY_ELECTIONS" | "TOO_FEW_HEALTHY_MEMBERS" | "TOO_MANY_UNHEALTHY_MEMBERS"; /** * Replication Specifications * @description Details that explain how MongoDB Cloud replicates data on the specified MongoDB database. @@ -3571,7 +4738,7 @@ export interface components { SearchIndexDefinitionVersion: { /** * Format: date-time - * @description The time at which this index definition was created. + * @description The time at which this index definition was created. This parameter expresses its value in the ISO 8601 timestamp format in UTC. */ createdAt?: string; /** @@ -3731,6 +4898,100 @@ export interface components { */ providerName: "AWS" | "AZURE"; }; + /** + * Stream Processor Alerts + * @description Stream Processor alert notifies about activities on Stream Processor in AtlasStreams. + */ + StreamProcessorAlertViewForNdsGroup: { + /** + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. + */ + acknowledgedUntil?: string; + /** + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. + */ + acknowledgementComment?: string; + /** + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. + */ + readonly acknowledgingUsername?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly alertConfigId: string; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly created: string; + eventTypeName: components["schemas"]["HostEventTypeViewForNdsGroupAlertable"]; + /** + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly id: string; + /** + * @description The name of the Stream Processing Instance to which this alert applies. The resource returns this parameter for alerts of events impacting Stream Processing Instances. + * @example foobar + */ + readonly instanceName?: string; + /** + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. + */ + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly orgId?: string; + /** + * @description The error message associated with the Stream Processor to which this alert applies. + * @example MongoServerError: Failed to start stream processor: (Location77175) Could not connect to the Kafka topic with kafka error code: -195, message: Local: Broker transport failure.: (Location77175) + */ + readonly processorErrorMsg?: string; + /** + * @description The name of the Stream Processor to which this alert applies. The resource returns this parameter for alerts of events impacting Stream Processors. + * @example foobar + */ + readonly processorName?: string; + /** + * @description The state of the Stream Processor to which this alert applies. The resource returns this parameter for alerts of events impacting Stream Processors. + * @example STARTED + */ + readonly processorState?: string; + /** + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. + */ + readonly resolved?: string; + /** + * @description State of this alert at the time you requested its details. + * @example OPEN + * @enum {string} + */ + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; + }; /** @description AWS configurations for AWS-based connection types. */ StreamsAWSConnectionConfig: { /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ @@ -3871,12 +5132,16 @@ export interface components { readonly links?: components["schemas"]["Link"][]; /** @description Reserved. Will be used by PRIVATE_LINK connection type. */ name?: string; + /** @description Reserved. Will be used by TRANSIT_GATEWAY connection type. */ + tgwId?: string; /** * Networking Access Type - * @description Selected networking type. Either PUBLIC, VPC or PRIVATE_LINK. Defaults to PUBLIC. For VPC, ensure that VPC peering exists and connectivity has been established between Atlas VPC and the VPC where Kafka cluster is hosted for the connection to function properly. PRIVATE_LINK support is coming soon. + * @description Selected networking type. Either PUBLIC, VPC, PRIVATE_LINK, or TRANSIT_GATEWAY. Defaults to PUBLIC. For VPC, ensure that VPC peering exists and connectivity has been established between Atlas VPC and the VPC where Kafka cluster is hosted for the connection to function properly. TRANSIT_GATEWAY support is coming soon. * @enum {string} */ - type?: "PUBLIC" | "VPC" | "PRIVATE_LINK"; + type?: "PUBLIC" | "VPC" | "PRIVATE_LINK" | "TRANSIT_GATEWAY"; + /** @description Reserved. Will be used by TRANSIT_GATEWAY connection type. */ + vpcCIDR?: string; }; /** @description Properties for the secure transport connection to Kafka. For SSL, this can include the trusted certificate to use. */ StreamsKafkaSecurity: { @@ -4107,61 +5372,175 @@ export interface components { * ] * } */ - storedSource?: Record; - /** @description Rule sets that map words to their synonyms in this index. */ - synonyms?: components["schemas"]["SearchSynonymMappingDefinition"][]; - }; - /** Text Search Index Response */ - TextSearchIndexResponse: Omit & { - latestDefinition?: components["schemas"]["TextSearchIndexDefinition"]; - /** @description List of documents detailing index status on each host. */ - statusDetail?: components["schemas"]["TextSearchHostStatusDetail"][]; + storedSource?: Record; + /** @description Rule sets that map words to their synonyms in this index. */ + synonyms?: components["schemas"]["SearchSynonymMappingDefinition"][]; + }; + /** Text Search Index Response */ + TextSearchIndexResponse: Omit & { + latestDefinition?: components["schemas"]["TextSearchIndexDefinition"]; + /** @description List of documents detailing index status on each host. */ + statusDetail?: components["schemas"]["TextSearchHostStatusDetail"][]; + /** + * @description Status that describes this index's synonym mappings. This status appears only if the index has synonyms defined. + * @enum {string} + */ + synonymMappingStatus?: "FAILED" | "BUILDING" | "READY"; + /** @description A list of documents describing the status of the index's synonym mappings on each search host. Only appears if the index has synonyms defined. */ + synonymMappingStatusDetail?: { + [key: string]: components["schemas"]["SynonymMappingStatusDetail"]; + }[]; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "search"; + }; + /** + * Text Search Index Status Detail + * @description Contains status information about a text search index. + */ + TextSearchIndexStatusDetail: { + definition?: components["schemas"]["TextSearchIndexDefinition"]; + definitionVersion?: components["schemas"]["SearchIndexDefinitionVersion"]; + /** @description Optional message describing an error. */ + message?: string; + /** @description Flag that indicates whether the index generation is queryable on the host. */ + queryable?: boolean; + /** + * @description Condition of the search index when you made this request. + * + * - `DELETING`: The index is being deleted. + * - `FAILED` The index build failed. Indexes can enter the FAILED state due to an invalid index definition. + * - `STALE`: The index is queryable but has stopped replicating data from the indexed collection. Searches on the index may return out-of-date data. + * - `PENDING`: Atlas has not yet started building the index. + * - `BUILDING`: Atlas is building or re-building the index after an edit. + * - `READY`: The index is ready and can support queries. + * @enum {string} + */ + status?: "DELETING" | "FAILED" | "STALE" | "PENDING" | "BUILDING" | "READY" | "DOES_NOT_EXIST"; + /** + * @description Status that describes this index's synonym mappings. This status appears only if the index has synonyms defined. + * @enum {string} + */ + synonymMappingStatus?: "FAILED" | "BUILDING" | "READY"; + /** @description List of synonym statuses by mapping. */ + synonymMappingStatusDetail?: components["schemas"]["SynonymMappingStatusDetailMap"][]; + }; + TimeMetricAlertView: { + /** + * Format: date-time + * @description Date and time until which this alert has been acknowledged. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if a MongoDB User previously acknowledged this alert. + * + * - To acknowledge this alert forever, set the parameter value to 100 years in the future. + * + * - To unacknowledge a previously acknowledged alert, do not set this parameter value. + */ + acknowledgedUntil?: string; + /** + * @description Comment that a MongoDB Cloud user submitted when acknowledging the alert. + * @example Expiration on 3/19. Silencing for 7days. + */ + acknowledgementComment?: string; + /** + * Format: email + * @description MongoDB Cloud username of the person who acknowledged the alert. The response returns this parameter if a MongoDB Cloud user previously acknowledged this alert. + */ + readonly acknowledgingUsername?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies the alert configuration that sets this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly alertConfigId: string; + /** + * @description Human-readable label that identifies the cluster to which this alert applies. This resource returns this parameter for alerts of events impacting backups, replica sets, or sharded clusters. + * @example cluster1 + */ + readonly clusterName?: string; + /** + * Format: date-time + * @description Date and time when MongoDB Cloud created this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly created: string; + currentValue?: components["schemas"]["TimeMetricValueView"]; + eventTypeName: components["schemas"]["HostMetricEventTypeViewAlertable"]; + /** + * @description Unique 24-hexadecimal digit string that identifies the project that owns this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly groupId?: string; + /** + * @description Hostname and port of the host to which this alert applies. The resource returns this parameter for alerts of events impacting hosts or replica sets. + * @example cloud-test.mongodb.com:27017 + */ + readonly hostnameAndPort?: string; + /** + * @description Unique 24-hexadecimal digit string that identifies this alert. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly id: string; + /** + * Format: date-time + * @description Date and time that any notifications were last sent for this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter if MongoDB Cloud has sent notifications for this alert. + */ + readonly lastNotified?: string; + /** @description List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships. */ + readonly links?: components["schemas"]["Link"][]; + /** + * @description Name of the metric against which Atlas checks the configured `metricThreshold.threshold`. + * + * To learn more about the available metrics, see Host Metrics. + * + * **NOTE**: If you set eventTypeName to OUTSIDE_SERVERLESS_METRIC_THRESHOLD, you can specify only metrics available for serverless. To learn more, see Serverless Measurements. + * @example ASSERT_USER + */ + readonly metricName?: string; + /** + * @description Unique 24-hexadecimal character string that identifies the organization that owns the project to which this alert applies. + * @example 32b6e34b3d91647abb20e7b8 + */ + readonly orgId?: string; + /** + * @description Name of the replica set to which this alert applies. The response returns this parameter for alerts of events impacting backups, hosts, or replica sets. + * @example event-replica-set + */ + readonly replicaSetName?: string; /** - * @description Status that describes this index's synonym mappings. This status appears only if the index has synonyms defined. - * @enum {string} + * Format: date-time + * @description Date and time that this alert changed to `"status" : "CLOSED"`. This parameter expresses its value in the ISO 8601 timestamp format in UTC. The resource returns this parameter once `"status" : "CLOSED"`. */ - synonymMappingStatus?: "FAILED" | "BUILDING" | "READY"; - /** @description A list of documents describing the status of the index's synonym mappings on each search host. Only appears if the index has synonyms defined. */ - synonymMappingStatusDetail?: { - [key: string]: components["schemas"]["SynonymMappingStatusDetail"]; - }[]; - } & { + readonly resolved?: string; /** - * @description discriminator enum property added by openapi-typescript + * @description State of this alert at the time you requested its details. + * @example OPEN * @enum {string} */ - type: "search"; + readonly status: "CANCELLED" | "CLOSED" | "OPEN" | "TRACKING"; + /** + * Format: date-time + * @description Date and time when someone last updated this alert. This parameter expresses its value in the ISO 8601 timestamp format in UTC. + */ + readonly updated: string; }; /** - * Text Search Index Status Detail - * @description Contains status information about a text search index. + * Time Metric Units + * @description Element used to express the quantity. This can be an element of time, storage capacity, and the like. + * @default HOURS + * @enum {string} */ - TextSearchIndexStatusDetail: { - definition?: components["schemas"]["TextSearchIndexDefinition"]; - definitionVersion?: components["schemas"]["SearchIndexDefinitionVersion"]; - /** @description Optional message describing an error. */ - message?: string; - /** @description Flag that indicates whether the index generation is queryable on the host. */ - queryable?: boolean; - /** - * @description Condition of the search index when you made this request. - * - * - `DELETING`: The index is being deleted. - * - `FAILED` The index build failed. Indexes can enter the FAILED state due to an invalid index definition. - * - `STALE`: The index is queryable but has stopped replicating data from the indexed collection. Searches on the index may return out-of-date data. - * - `PENDING`: Atlas has not yet started building the index. - * - `BUILDING`: Atlas is building or re-building the index after an edit. - * - `READY`: The index is ready and can support queries. - * @enum {string} - */ - status?: "DELETING" | "FAILED" | "STALE" | "PENDING" | "BUILDING" | "READY" | "DOES_NOT_EXIST"; + TimeMetricUnits: "NANOSECONDS" | "MILLISECONDS" | "MILLION_MINUTES" | "SECONDS" | "MINUTES" | "HOURS" | "DAYS"; + /** + * Time Metric Value + * @description Measurement of the **metricName** recorded at the time of the event. + */ + TimeMetricValueView: { /** - * @description Status that describes this index's synonym mappings. This status appears only if the index has synonyms defined. - * @enum {string} + * Format: double + * @description Amount of the **metricName** recorded at the time of the event. This value triggered the alert. */ - synonymMappingStatus?: "FAILED" | "BUILDING" | "READY"; - /** @description List of synonym statuses by mapping. */ - synonymMappingStatusDetail?: components["schemas"]["SynonymMappingStatusDetailMap"][]; + readonly number?: number; + units?: components["schemas"]["TimeMetricUnits"]; }; /** * englishPossessive @@ -4954,11 +6333,14 @@ export type AwsRegionConfig = components['schemas']['AWSRegionConfig']; export type AwsRegionConfig20240805 = components['schemas']['AWSRegionConfig20240805']; export type AdvancedAutoScalingSettings = components['schemas']['AdvancedAutoScalingSettings']; export type AdvancedComputeAutoScaling = components['schemas']['AdvancedComputeAutoScaling']; +export type AlertViewForNdsGroup = components['schemas']['AlertViewForNdsGroup']; export type ApiAtlasCloudProviderAccessFeatureUsageFeatureIdView = components['schemas']['ApiAtlasCloudProviderAccessFeatureUsageFeatureIdView']; export type ApiAtlasClusterAdvancedConfigurationView = components['schemas']['ApiAtlasClusterAdvancedConfigurationView']; export type ApiAtlasFtsAnalyzersViewManual = components['schemas']['ApiAtlasFTSAnalyzersViewManual']; export type ApiAtlasFtsMappingsViewManual = components['schemas']['ApiAtlasFTSMappingsViewManual']; export type ApiError = components['schemas']['ApiError']; +export type AppServiceAlertView = components['schemas']['AppServiceAlertView']; +export type AppServiceEventTypeViewAlertable = components['schemas']['AppServiceEventTypeViewAlertable']; export type AtlasOrganization = components['schemas']['AtlasOrganization']; export type AtlasSearchAnalyzer = components['schemas']['AtlasSearchAnalyzer']; export type AzureCloudProviderContainer = components['schemas']['AzureCloudProviderContainer']; @@ -5003,10 +6385,12 @@ export type CloudProviderContainer = components['schemas']['CloudProviderContain export type CloudProviderGcpAutoScaling = components['schemas']['CloudProviderGCPAutoScaling']; export type CloudRegionConfig = components['schemas']['CloudRegionConfig']; export type CloudRegionConfig20240805 = components['schemas']['CloudRegionConfig20240805']; +export type ClusterAlertView = components['schemas']['ClusterAlertView']; export type ClusterConnectionStrings = components['schemas']['ClusterConnectionStrings']; export type ClusterDescription20240805 = components['schemas']['ClusterDescription20240805']; export type ClusterDescriptionConnectionStringsPrivateEndpoint = components['schemas']['ClusterDescriptionConnectionStringsPrivateEndpoint']; export type ClusterDescriptionConnectionStringsPrivateEndpointEndpoint = components['schemas']['ClusterDescriptionConnectionStringsPrivateEndpointEndpoint']; +export type ClusterEventTypeViewAlertable = components['schemas']['ClusterEventTypeViewAlertable']; export type ClusterFlexProviderSettings = components['schemas']['ClusterFlexProviderSettings']; export type ClusterFreeAutoScaling = components['schemas']['ClusterFreeAutoScaling']; export type ClusterFreeProviderSettings = components['schemas']['ClusterFreeProviderSettings']; @@ -5037,11 +6421,15 @@ export type DataLakeHttpStore = components['schemas']['DataLakeHTTPStore']; export type DataLakePipelinesPartitionField = components['schemas']['DataLakePipelinesPartitionField']; export type DataLakeS3StoreSettings = components['schemas']['DataLakeS3StoreSettings']; export type DataLakeStoreSettings = components['schemas']['DataLakeStoreSettings']; +export type DataMetricAlertView = components['schemas']['DataMetricAlertView']; +export type DataMetricUnits = components['schemas']['DataMetricUnits']; +export type DataMetricValueView = components['schemas']['DataMetricValueView']; export type DataProcessRegionView = components['schemas']['DataProcessRegionView']; export type DatabaseUserRole = components['schemas']['DatabaseUserRole']; export type DateCriteriaView = components['schemas']['DateCriteriaView']; export type DedicatedHardwareSpec = components['schemas']['DedicatedHardwareSpec']; export type DedicatedHardwareSpec20240805 = components['schemas']['DedicatedHardwareSpec20240805']; +export type DefaultAlertViewForNdsGroup = components['schemas']['DefaultAlertViewForNdsGroup']; export type DefaultScheduleView = components['schemas']['DefaultScheduleView']; export type DiskBackupSnapshotAwsExportBucketRequest = components['schemas']['DiskBackupSnapshotAWSExportBucketRequest']; export type DiskBackupSnapshotAwsExportBucketResponse = components['schemas']['DiskBackupSnapshotAWSExportBucketResponse']; @@ -5049,10 +6437,18 @@ export type DiskBackupSnapshotAzureExportBucketRequest = components['schemas'][' export type DiskBackupSnapshotAzureExportBucketResponse = components['schemas']['DiskBackupSnapshotAzureExportBucketResponse']; export type DiskBackupSnapshotExportBucketRequest = components['schemas']['DiskBackupSnapshotExportBucketRequest']; export type DiskBackupSnapshotExportBucketResponse = components['schemas']['DiskBackupSnapshotExportBucketResponse']; +export type DiskBackupSnapshotGcpExportBucketRequest = components['schemas']['DiskBackupSnapshotGCPExportBucketRequest']; +export type DiskBackupSnapshotGcpExportBucketResponse = components['schemas']['DiskBackupSnapshotGCPExportBucketResponse']; export type DiskGbAutoScaling = components['schemas']['DiskGBAutoScaling']; export type EmployeeAccessGrantView = components['schemas']['EmployeeAccessGrantView']; export type FieldViolation = components['schemas']['FieldViolation']; export type Fields = components['schemas']['Fields']; +export type FlexBackupSettings20241113 = components['schemas']['FlexBackupSettings20241113']; +export type FlexClusterDescription20241113 = components['schemas']['FlexClusterDescription20241113']; +export type FlexClusterDescriptionCreate20241113 = components['schemas']['FlexClusterDescriptionCreate20241113']; +export type FlexConnectionStrings20241113 = components['schemas']['FlexConnectionStrings20241113']; +export type FlexProviderSettings20241113 = components['schemas']['FlexProviderSettings20241113']; +export type FlexProviderSettingsCreate20241113 = components['schemas']['FlexProviderSettingsCreate20241113']; export type FreeComputeAutoScalingRules = components['schemas']['FreeComputeAutoScalingRules']; export type GcpCloudProviderContainer = components['schemas']['GCPCloudProviderContainer']; export type GcpComputeAutoScaling = components['schemas']['GCPComputeAutoScaling']; @@ -5069,12 +6465,20 @@ export type GroupRoleAssignment = components['schemas']['GroupRoleAssignment']; export type GroupUserResponse = components['schemas']['GroupUserResponse']; export type HardwareSpec = components['schemas']['HardwareSpec']; export type HardwareSpec20240805 = components['schemas']['HardwareSpec20240805']; +export type HostAlertViewForNdsGroup = components['schemas']['HostAlertViewForNdsGroup']; +export type HostEventTypeViewForNdsGroupAlertable = components['schemas']['HostEventTypeViewForNdsGroupAlertable']; +export type HostMetricAlert = components['schemas']['HostMetricAlert']; +export type HostMetricEventTypeViewAlertable = components['schemas']['HostMetricEventTypeViewAlertable']; +export type HostMetricValue = components['schemas']['HostMetricValue']; export type IngestionSink = components['schemas']['IngestionSink']; export type IngestionSource = components['schemas']['IngestionSource']; export type InvoiceLineItem = components['schemas']['InvoiceLineItem']; export type Link = components['schemas']['Link']; export type MonthlyScheduleView = components['schemas']['MonthlyScheduleView']; export type NetworkPermissionEntry = components['schemas']['NetworkPermissionEntry']; +export type NumberMetricAlertView = components['schemas']['NumberMetricAlertView']; +export type NumberMetricUnits = components['schemas']['NumberMetricUnits']; +export type NumberMetricValueView = components['schemas']['NumberMetricValueView']; export type OnDemandCpsSnapshotSource = components['schemas']['OnDemandCpsSnapshotSource']; export type OnlineArchiveSchedule = components['schemas']['OnlineArchiveSchedule']; export type OrgActiveUserResponse = components['schemas']['OrgActiveUserResponse']; @@ -5082,13 +6486,20 @@ export type OrgGroup = components['schemas']['OrgGroup']; export type OrgPendingUserResponse = components['schemas']['OrgPendingUserResponse']; export type OrgUserResponse = components['schemas']['OrgUserResponse']; export type OrgUserRolesResponse = components['schemas']['OrgUserRolesResponse']; +export type PaginatedAlertView = components['schemas']['PaginatedAlertView']; export type PaginatedApiAtlasDatabaseUserView = components['schemas']['PaginatedApiAtlasDatabaseUserView']; export type PaginatedAtlasGroupView = components['schemas']['PaginatedAtlasGroupView']; export type PaginatedClusterDescription20240805 = components['schemas']['PaginatedClusterDescription20240805']; +export type PaginatedFlexClusters20241113 = components['schemas']['PaginatedFlexClusters20241113']; export type PaginatedNetworkAccessView = components['schemas']['PaginatedNetworkAccessView']; export type PaginatedOrgGroupView = components['schemas']['PaginatedOrgGroupView']; export type PaginatedOrganizationView = components['schemas']['PaginatedOrganizationView']; export type PeriodicCpsSnapshotSource = components['schemas']['PeriodicCpsSnapshotSource']; +export type RawMetricAlertView = components['schemas']['RawMetricAlertView']; +export type RawMetricUnits = components['schemas']['RawMetricUnits']; +export type RawMetricValueView = components['schemas']['RawMetricValueView']; +export type ReplicaSetAlertViewForNdsGroup = components['schemas']['ReplicaSetAlertViewForNdsGroup']; +export type ReplicaSetEventTypeViewForNdsGroupAlertable = components['schemas']['ReplicaSetEventTypeViewForNdsGroupAlertable']; export type ReplicationSpec20240805 = components['schemas']['ReplicationSpec20240805']; export type ResourceTag = components['schemas']['ResourceTag']; export type SearchHostStatusDetail = components['schemas']['SearchHostStatusDetail']; @@ -5104,6 +6515,7 @@ export type SearchSynonymMappingDefinition = components['schemas']['SearchSynony export type ServerlessAwsTenantEndpointUpdate = components['schemas']['ServerlessAWSTenantEndpointUpdate']; export type ServerlessAzureTenantEndpointUpdate = components['schemas']['ServerlessAzureTenantEndpointUpdate']; export type ServerlessTenantEndpointUpdate = components['schemas']['ServerlessTenantEndpointUpdate']; +export type StreamProcessorAlertViewForNdsGroup = components['schemas']['StreamProcessorAlertViewForNdsGroup']; export type StreamsAwsConnectionConfig = components['schemas']['StreamsAWSConnectionConfig']; export type StreamsAwsLambdaConnection = components['schemas']['StreamsAWSLambdaConnection']; export type StreamsClusterConnection = components['schemas']['StreamsClusterConnection']; @@ -5128,6 +6540,9 @@ export type TextSearchIndexCreateRequest = components['schemas']['TextSearchInde export type TextSearchIndexDefinition = components['schemas']['TextSearchIndexDefinition']; export type TextSearchIndexResponse = components['schemas']['TextSearchIndexResponse']; export type TextSearchIndexStatusDetail = components['schemas']['TextSearchIndexStatusDetail']; +export type TimeMetricAlertView = components['schemas']['TimeMetricAlertView']; +export type TimeMetricUnits = components['schemas']['TimeMetricUnits']; +export type TimeMetricValueView = components['schemas']['TimeMetricValueView']; export type TokenFilterEnglishPossessive = components['schemas']['TokenFilterEnglishPossessive']; export type TokenFilterFlattenGraph = components['schemas']['TokenFilterFlattenGraph']; export type TokenFilterPorterStemming = components['schemas']['TokenFilterPorterStemming']; @@ -5215,6 +6630,7 @@ export interface operations { }; }; 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; 500: components["responses"]["internalServerError"]; }; }; @@ -5248,6 +6664,8 @@ export interface operations { }; }; 400: components["responses"]["badRequest"]; + 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; 404: components["responses"]["notFound"]; 500: components["responses"]["internalServerError"]; }; @@ -5319,6 +6737,8 @@ export interface operations { }; }; 400: components["responses"]["badRequest"]; + 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; 404: components["responses"]["notFound"]; 500: components["responses"]["internalServerError"]; }; @@ -5352,6 +6772,8 @@ export interface operations { }; }; 400: components["responses"]["badRequest"]; + 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; 404: components["responses"]["notFound"]; 409: components["responses"]["conflict"]; 500: components["responses"]["internalServerError"]; @@ -5392,6 +6814,8 @@ export interface operations { }; }; 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; 500: components["responses"]["internalServerError"]; }; }; @@ -5437,6 +6861,7 @@ export interface operations { 400: components["responses"]["badRequest"]; 401: components["responses"]["unauthorized"]; 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; 500: components["responses"]["internalServerError"]; }; }; @@ -5474,6 +6899,50 @@ export interface operations { "application/vnd.atlas.2023-01-01+json": unknown; }; }; + 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; + 500: components["responses"]["internalServerError"]; + }; + }; + listAlerts: { + parameters: { + query?: { + /** @description Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body. */ + envelope?: components["parameters"]["envelope"]; + /** @description Flag that indicates whether the response returns the total number of items (**totalCount**) in the response. */ + includeCount?: components["parameters"]["includeCount"]; + /** @description Number of items that the response returns per page. */ + itemsPerPage?: components["parameters"]["itemsPerPage"]; + /** @description Number of the page that displays the current set of the total objects that the response returns. */ + pageNum?: components["parameters"]["pageNum"]; + /** @description Flag that indicates whether the response body should be in the prettyprint format. */ + pretty?: components["parameters"]["pretty"]; + /** @description Status of the alerts to return. Omit to return all alerts in all statuses. */ + status?: "OPEN" | "TRACKING" | "CLOSED"; + }; + header?: never; + path: { + /** @description Unique 24-hexadecimal digit string that identifies your project. Use the [/groups](#tag/Projects/operation/listProjects) endpoint to retrieve all projects to which the authenticated user has access. + * + * **NOTE**: Groups and projects are synonymous terms. Your group id is the same as your project id. For existing groups, your group/project id remains the same. The resource and corresponding endpoints use the term groups. */ + groupId: components["parameters"]["groupId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/vnd.atlas.2023-01-01+json": components["schemas"]["PaginatedAlertView"]; + }; + }; + 400: components["responses"]["badRequest"]; + 401: components["responses"]["unauthorized"]; 403: components["responses"]["forbidden"]; 404: components["responses"]["notFound"]; 500: components["responses"]["internalServerError"]; @@ -5516,6 +6985,8 @@ export interface operations { }; }; 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; 500: components["responses"]["internalServerError"]; }; }; @@ -5556,6 +7027,7 @@ export interface operations { 401: components["responses"]["unauthorized"]; 402: components["responses"]["paymentRequired"]; 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; 409: components["responses"]["conflict"]; 500: components["responses"]["internalServerError"]; }; @@ -5591,6 +7063,7 @@ export interface operations { }; }; 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; 404: components["responses"]["notFound"]; 409: components["responses"]["conflict"]; 500: components["responses"]["internalServerError"]; @@ -5630,6 +7103,7 @@ export interface operations { }; 400: components["responses"]["badRequest"]; 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; 404: components["responses"]["notFound"]; 409: components["responses"]["conflict"]; 500: components["responses"]["internalServerError"]; @@ -5670,6 +7144,8 @@ export interface operations { }; }; 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; 500: components["responses"]["internalServerError"]; }; }; @@ -5765,6 +7241,165 @@ export interface operations { 500: components["responses"]["internalServerError"]; }; }; + listFlexClusters: { + parameters: { + query?: { + /** @description Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body. */ + envelope?: components["parameters"]["envelope"]; + /** @description Flag that indicates whether the response returns the total number of items (**totalCount**) in the response. */ + includeCount?: components["parameters"]["includeCount"]; + /** @description Number of items that the response returns per page. */ + itemsPerPage?: components["parameters"]["itemsPerPage"]; + /** @description Number of the page that displays the current set of the total objects that the response returns. */ + pageNum?: components["parameters"]["pageNum"]; + /** @description Flag that indicates whether the response body should be in the prettyprint format. */ + pretty?: components["parameters"]["pretty"]; + }; + header?: never; + path: { + /** @description Unique 24-hexadecimal digit string that identifies your project. Use the [/groups](#tag/Projects/operation/listProjects) endpoint to retrieve all projects to which the authenticated user has access. + * + * **NOTE**: Groups and projects are synonymous terms. Your group id is the same as your project id. For existing groups, your group/project id remains the same. The resource and corresponding endpoints use the term groups. */ + groupId: components["parameters"]["groupId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/vnd.atlas.2024-11-13+json": components["schemas"]["PaginatedFlexClusters20241113"]; + }; + }; + 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; + 409: components["responses"]["conflict"]; + 500: components["responses"]["internalServerError"]; + }; + }; + createFlexCluster: { + parameters: { + query?: { + /** @description Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body. */ + envelope?: components["parameters"]["envelope"]; + /** @description Flag that indicates whether the response body should be in the prettyprint format. */ + pretty?: components["parameters"]["pretty"]; + }; + header?: never; + path: { + /** @description Unique 24-hexadecimal digit string that identifies your project. Use the [/groups](#tag/Projects/operation/listProjects) endpoint to retrieve all projects to which the authenticated user has access. + * + * **NOTE**: Groups and projects are synonymous terms. Your group id is the same as your project id. For existing groups, your group/project id remains the same. The resource and corresponding endpoints use the term groups. */ + groupId: components["parameters"]["groupId"]; + }; + cookie?: never; + }; + /** @description Create One Flex Cluster in One Project. */ + requestBody: { + content: { + "application/vnd.atlas.2024-11-13+json": components["schemas"]["FlexClusterDescriptionCreate20241113"]; + }; + }; + responses: { + /** @description Created */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/vnd.atlas.2024-11-13+json": components["schemas"]["FlexClusterDescription20241113"]; + }; + }; + 400: components["responses"]["badRequest"]; + 401: components["responses"]["unauthorized"]; + 402: components["responses"]["paymentRequired"]; + 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; + 409: components["responses"]["conflict"]; + 500: components["responses"]["internalServerError"]; + }; + }; + getFlexCluster: { + parameters: { + query?: { + /** @description Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body. */ + envelope?: components["parameters"]["envelope"]; + /** @description Flag that indicates whether the response body should be in the prettyprint format. */ + pretty?: components["parameters"]["pretty"]; + }; + header?: never; + path: { + /** @description Unique 24-hexadecimal digit string that identifies your project. Use the [/groups](#tag/Projects/operation/listProjects) endpoint to retrieve all projects to which the authenticated user has access. + * + * **NOTE**: Groups and projects are synonymous terms. Your group id is the same as your project id. For existing groups, your group/project id remains the same. The resource and corresponding endpoints use the term groups. */ + groupId: components["parameters"]["groupId"]; + /** @description Human-readable label that identifies the flex cluster. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/vnd.atlas.2024-11-13+json": components["schemas"]["FlexClusterDescription20241113"]; + }; + }; + 400: components["responses"]["badRequest"]; + 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; + 409: components["responses"]["conflict"]; + 500: components["responses"]["internalServerError"]; + }; + }; + deleteFlexCluster: { + parameters: { + query?: { + /** @description Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body. */ + envelope?: components["parameters"]["envelope"]; + /** @description Flag that indicates whether the response body should be in the prettyprint format. */ + pretty?: components["parameters"]["pretty"]; + }; + header?: never; + path: { + /** @description Unique 24-hexadecimal digit string that identifies your project. Use the [/groups](#tag/Projects/operation/listProjects) endpoint to retrieve all projects to which the authenticated user has access. + * + * **NOTE**: Groups and projects are synonymous terms. Your group id is the same as your project id. For existing groups, your group/project id remains the same. The resource and corresponding endpoints use the term groups. */ + groupId: components["parameters"]["groupId"]; + /** @description Human-readable label that identifies the flex cluster. */ + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description This endpoint does not return a response body. */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/vnd.atlas.2024-11-13+json": unknown; + }; + }; + 400: components["responses"]["badRequest"]; + 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; + 404: components["responses"]["notFound"]; + 409: components["responses"]["conflict"]; + 500: components["responses"]["internalServerError"]; + }; + }; listOrganizations: { parameters: { query?: { @@ -5798,6 +7433,7 @@ export interface operations { }; 400: components["responses"]["badRequest"]; 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; 404: components["responses"]["notFound"]; 409: components["responses"]["conflict"]; 500: components["responses"]["internalServerError"]; @@ -5839,6 +7475,7 @@ export interface operations { }; 400: components["responses"]["badRequest"]; 401: components["responses"]["unauthorized"]; + 403: components["responses"]["forbidden"]; 404: components["responses"]["notFound"]; 500: components["responses"]["internalServerError"]; }; diff --git a/src/config.ts b/src/config.ts index cea589ba..9be54452 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,25 +4,29 @@ import argv from "yargs-parser"; import { ReadConcernLevel, ReadPreferenceMode, W } from "mongodb"; +export interface ConnectOptions { + readConcern: ReadConcernLevel; + readPreference: ReadPreferenceMode; + writeConcern: W; + timeoutMS: number; +} + // If we decide to support non-string config options, we'll need to extend the mechanism for parsing // env variables. export interface UserConfig { - apiBaseUrl?: string; + apiBaseUrl: string; apiClientId?: string; apiClientSecret?: string; telemetry?: "enabled" | "disabled"; logPath: string; connectionString?: string; - connectOptions: { - readConcern: ReadConcernLevel; - readPreference: ReadPreferenceMode; - writeConcern: W; - timeoutMS: number; - }; + connectOptions: ConnectOptions; disabledTools: Array; + readOnly?: boolean; } const defaults: UserConfig = { + apiBaseUrl: "https://cloud.mongodb.com/", logPath: getLogPath(), connectOptions: { readConcern: "local", @@ -31,6 +35,8 @@ const defaults: UserConfig = { timeoutMS: 30_000, }, disabledTools: [], + telemetry: "enabled", + readOnly: false, }; export const config = { diff --git a/src/errors.ts b/src/errors.ts index 224610fb..ae91c3a0 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,6 +1,6 @@ export enum ErrorCodes { NotConnectedToMongoDB = 1_000_000, - InvalidParams = 1_000_001, + MisconfiguredConnectionString = 1_000_001, } export class MongoDBError extends Error { diff --git a/src/helpers/EJsonTransport.ts b/src/helpers/EJsonTransport.ts new file mode 100644 index 00000000..307e90bd --- /dev/null +++ b/src/helpers/EJsonTransport.ts @@ -0,0 +1,47 @@ +import { JSONRPCMessage, JSONRPCMessageSchema } from "@modelcontextprotocol/sdk/types.js"; +import { EJSON } from "bson"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; + +// This is almost a copy of ReadBuffer from @modelcontextprotocol/sdk +// but it uses EJSON.parse instead of JSON.parse to handle BSON types +export class EJsonReadBuffer { + private _buffer?: Buffer; + + append(chunk: Buffer): void { + this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk; + } + + readMessage(): JSONRPCMessage | null { + if (!this._buffer) { + return null; + } + + const index = this._buffer.indexOf("\n"); + if (index === -1) { + return null; + } + + const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, ""); + this._buffer = this._buffer.subarray(index + 1); + + // This is using EJSON.parse instead of JSON.parse to handle BSON types + return JSONRPCMessageSchema.parse(EJSON.parse(line)); + } + + clear(): void { + this._buffer = undefined; + } +} + +// This is a hacky workaround for https://github.com/mongodb-js/mongodb-mcp-server/issues/211 +// The underlying issue is that StdioServerTransport uses JSON.parse to deserialize +// messages, but that doesn't handle bson types, such as ObjectId when serialized as EJSON. +// +// This function creates a StdioServerTransport and replaces the internal readBuffer with EJsonReadBuffer +// that uses EJson.parse instead. +export function createEJsonTransport(): StdioServerTransport { + const server = new StdioServerTransport(); + server["_readBuffer"] = new EJsonReadBuffer(); + + return server; +} diff --git a/src/helpers/connectionOptions.ts b/src/helpers/connectionOptions.ts new file mode 100644 index 00000000..10b1ecc8 --- /dev/null +++ b/src/helpers/connectionOptions.ts @@ -0,0 +1,20 @@ +import { MongoClientOptions } from "mongodb"; +import ConnectionString from "mongodb-connection-string-url"; + +export function setAppNameParamIfMissing({ + connectionString, + defaultAppName, +}: { + connectionString: string; + defaultAppName?: string; +}): string { + const connectionStringUrl = new ConnectionString(connectionString); + + const searchParams = connectionStringUrl.typedSearchParams(); + + if (!searchParams.has("appName") && defaultAppName !== undefined) { + searchParams.set("appName", defaultAppName); + } + + return connectionStringUrl.toString(); +} diff --git a/src/packageInfo.ts b/src/helpers/packageInfo.ts similarity index 61% rename from src/packageInfo.ts rename to src/helpers/packageInfo.ts index dea9214b..6c075dc0 100644 --- a/src/packageInfo.ts +++ b/src/helpers/packageInfo.ts @@ -1,4 +1,4 @@ -import packageJson from "../package.json" with { type: "json" }; +import packageJson from "../../package.json" with { type: "json" }; export const packageInfo = { version: packageJson.version, diff --git a/src/index.ts b/src/index.ts index 268c4803..02f9ca36 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,13 @@ #!/usr/bin/env node -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import logger from "./logger.js"; -import { mongoLogId } from "mongodb-log-writer"; +import logger, { LogId } from "./logger.js"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { config } from "./config.js"; import { Session } from "./session.js"; import { Server } from "./server.js"; -import { packageInfo } from "./packageInfo.js"; +import { packageInfo } from "./helpers/packageInfo.js"; +import { Telemetry } from "./telemetry/telemetry.js"; +import { createEJsonTransport } from "./helpers/EJsonTransport.js"; try { const session = new Session({ @@ -19,16 +19,40 @@ try { name: packageInfo.mcpServerName, version: packageInfo.version, }); + + const telemetry = Telemetry.create(session, config); + const server = new Server({ mcpServer, session, + telemetry, userConfig: config, }); - const transport = new StdioServerTransport(); + const transport = createEJsonTransport(); + + const shutdown = () => { + logger.info(LogId.serverCloseRequested, "server", `Server close requested`); + + server + .close() + .then(() => { + logger.info(LogId.serverClosed, "server", `Server closed successfully`); + process.exit(0); + }) + .catch((err: unknown) => { + const error = err instanceof Error ? err : new Error(String(err)); + logger.error(LogId.serverCloseFailure, "server", `Error closing server: ${error.message}`); + process.exit(1); + }); + }; + + process.once("SIGINT", shutdown); + process.once("SIGTERM", shutdown); + process.once("SIGQUIT", shutdown); await server.connect(transport); } catch (error: unknown) { - logger.emergency(mongoLogId(1_000_004), "server", `Fatal error running server: ${error as string}`); + logger.emergency(LogId.serverStartFailure, "server", `Fatal error running server: ${error as string}`); process.exit(1); } diff --git a/src/logger.ts b/src/logger.ts index 425f56b9..7adf1263 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,13 +1,45 @@ import fs from "fs/promises"; -import { MongoLogId, MongoLogManager, MongoLogWriter } from "mongodb-log-writer"; +import { mongoLogId, MongoLogId, MongoLogManager, MongoLogWriter } from "mongodb-log-writer"; import redact from "mongodb-redact"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { LoggingMessageNotification } from "@modelcontextprotocol/sdk/types.js"; export type LogLevel = LoggingMessageNotification["params"]["level"]; +export const LogId = { + serverStartFailure: mongoLogId(1_000_001), + serverInitialized: mongoLogId(1_000_002), + serverCloseRequested: mongoLogId(1_000_003), + serverClosed: mongoLogId(1_000_004), + serverCloseFailure: mongoLogId(1_000_005), + + atlasCheckCredentials: mongoLogId(1_001_001), + atlasDeleteDatabaseUserFailure: mongoLogId(1_001_002), + atlasConnectFailure: mongoLogId(1_001_003), + atlasInspectFailure: mongoLogId(1_001_004), + + telemetryDisabled: mongoLogId(1_002_001), + telemetryEmitFailure: mongoLogId(1_002_002), + telemetryEmitStart: mongoLogId(1_002_003), + telemetryEmitSuccess: mongoLogId(1_002_004), + telemetryMetadataError: mongoLogId(1_002_005), + telemetryDeviceIdFailure: mongoLogId(1_002_006), + telemetryDeviceIdTimeout: mongoLogId(1_002_007), + telemetryContainerEnvFailure: mongoLogId(1_002_008), + + toolExecute: mongoLogId(1_003_001), + toolExecuteFailure: mongoLogId(1_003_002), + toolDisabled: mongoLogId(1_003_003), + + mongodbConnectFailure: mongoLogId(1_004_001), + mongodbDisconnectFailure: mongoLogId(1_004_002), + + toolUpdateFailure: mongoLogId(1_005_001), +} as const; + abstract class LoggerBase { abstract log(level: LogLevel, id: MongoLogId, context: string, message: string): void; + info(id: MongoLogId, context: string, message: string): void { this.log("info", id, context, message); } @@ -47,22 +79,35 @@ class ConsoleLogger extends LoggerBase { } } -class Logger extends LoggerBase { - constructor( - private logWriter: MongoLogWriter, - private server: McpServer - ) { +class DiskLogger extends LoggerBase { + private constructor(private logWriter: MongoLogWriter) { super(); } + static async fromPath(logPath: string): Promise { + await fs.mkdir(logPath, { recursive: true }); + + const manager = new MongoLogManager({ + directory: logPath, + retentionDays: 30, + onwarn: console.warn, + onerror: console.error, + gzip: false, + retentionGB: 1, + }); + + await manager.cleanupOldLogFiles(); + + const logWriter = await manager.createLogWriter(); + + return new DiskLogger(logWriter); + } + log(level: LogLevel, id: MongoLogId, context: string, message: string): void { message = redact(message); const mongoDBLevel = this.mapToMongoDBLogLevel(level); + this.logWriter[mongoDBLevel]("MONGODB-MCP", id, context, message); - void this.server.server.sendLoggingMessage({ - level, - data: `[${context}]: ${message}`, - }); } private mapToMongoDBLogLevel(level: LogLevel): "info" | "warn" | "error" | "debug" | "fatal" { @@ -86,31 +131,61 @@ class Logger extends LoggerBase { } } -class ProxyingLogger extends LoggerBase { - private internalLogger: LoggerBase = new ConsoleLogger(); +class McpLogger extends LoggerBase { + constructor(private server: McpServer) { + super(); + } + + log(level: LogLevel, _: MongoLogId, context: string, message: string): void { + // Only log if the server is connected + if (!this.server?.isConnected()) { + return; + } + + void this.server.server.sendLoggingMessage({ + level, + data: `[${context}]: ${message}`, + }); + } +} + +class CompositeLogger extends LoggerBase { + private loggers: LoggerBase[]; + + constructor(...loggers: LoggerBase[]) { + super(); + + if (loggers.length === 0) { + // default to ConsoleLogger + this.loggers = [new ConsoleLogger()]; + return; + } + + this.loggers = [...loggers]; + } + + setLoggers(...loggers: LoggerBase[]): void { + if (loggers.length === 0) { + throw new Error("At least one logger must be provided"); + } + this.loggers = [...loggers]; + } log(level: LogLevel, id: MongoLogId, context: string, message: string): void { - this.internalLogger.log(level, id, context, message); + for (const logger of this.loggers) { + logger.log(level, id, context, message); + } } } -const logger = new ProxyingLogger(); +const logger = new CompositeLogger(); export default logger; -export async function initializeLogger(server: McpServer, logPath: string): Promise { - await fs.mkdir(logPath, { recursive: true }); - - const manager = new MongoLogManager({ - directory: logPath, - retentionDays: 30, - onwarn: console.warn, - onerror: console.error, - gzip: false, - retentionGB: 1, - }); +export async function initializeLogger(server: McpServer, logPath: string): Promise { + const diskLogger = await DiskLogger.fromPath(logPath); + const mcpLogger = new McpLogger(server); - await manager.cleanupOldLogFiles(); + logger.setLoggers(mcpLogger, diskLogger); - const logWriter = await manager.createLogWriter(); - logger["internalLogger"] = new Logger(logWriter, server); + return logger; } diff --git a/src/server.ts b/src/server.ts index fd16c75d..9012fdf5 100644 --- a/src/server.ts +++ b/src/server.ts @@ -3,36 +3,66 @@ import { Session } from "./session.js"; import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { AtlasTools } from "./tools/atlas/tools.js"; import { MongoDbTools } from "./tools/mongodb/tools.js"; -import logger, { initializeLogger } from "./logger.js"; -import { mongoLogId } from "mongodb-log-writer"; +import logger, { initializeLogger, LogId } from "./logger.js"; import { ObjectId } from "mongodb"; import { Telemetry } from "./telemetry/telemetry.js"; import { UserConfig } from "./config.js"; +import { type ServerEvent } from "./telemetry/types.js"; +import { type ServerCommand } from "./telemetry/types.js"; +import { CallToolRequestSchema, CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import assert from "assert"; export interface ServerOptions { session: Session; userConfig: UserConfig; mcpServer: McpServer; + telemetry: Telemetry; } export class Server { public readonly session: Session; private readonly mcpServer: McpServer; private readonly telemetry: Telemetry; - private readonly userConfig: UserConfig; + public readonly userConfig: UserConfig; + private readonly startTime: number; - constructor({ session, mcpServer, userConfig }: ServerOptions) { + constructor({ session, mcpServer, userConfig, telemetry }: ServerOptions) { + this.startTime = Date.now(); this.session = session; - this.telemetry = new Telemetry(session); + this.telemetry = telemetry; this.mcpServer = mcpServer; this.userConfig = userConfig; } - async connect(transport: Transport) { + async connect(transport: Transport): Promise { this.mcpServer.server.registerCapabilities({ logging: {} }); + this.registerTools(); this.registerResources(); + // This is a workaround for an issue we've seen with some models, where they'll see that everything in the `arguments` + // object is optional, and then not pass it at all. However, the MCP server expects the `arguments` object to be if + // the tool accepts any arguments, even if they're all optional. + // + // see: https://github.com/modelcontextprotocol/typescript-sdk/blob/131776764536b5fdca642df51230a3746fb4ade0/src/server/mcp.ts#L705 + // Since paramsSchema here is not undefined, the server will create a non-optional z.object from it. + const existingHandler = ( + this.mcpServer.server["_requestHandlers"] as Map< + string, + (request: unknown, extra: unknown) => Promise + > + ).get(CallToolRequestSchema.shape.method.value); + + assert(existingHandler, "No existing handler found for CallToolRequestSchema"); + + this.mcpServer.server.setRequestHandler(CallToolRequestSchema, (request, extra): Promise => { + if (!request.params.arguments) { + request.params.arguments = {}; + } + + return existingHandler(request, extra); + }); + await initializeLogger(this.mcpServer, this.userConfig.logPath); await this.mcpServer.connect(transport); @@ -42,18 +72,67 @@ export class Server { this.session.sessionId = new ObjectId().toString(); logger.info( - mongoLogId(1_000_004), + LogId.serverInitialized, "server", `Server started with transport ${transport.constructor.name} and agent runner ${this.session.agentRunner?.name}` ); + + this.emitServerEvent("start", Date.now() - this.startTime); }; + + this.mcpServer.server.onclose = () => { + const closeTime = Date.now(); + this.emitServerEvent("stop", Date.now() - closeTime); + }; + + this.mcpServer.server.onerror = (error: Error) => { + const closeTime = Date.now(); + this.emitServerEvent("stop", Date.now() - closeTime, error); + }; + + await this.validateConfig(); } async close(): Promise { + await this.telemetry.close(); await this.session.close(); await this.mcpServer.close(); } + /** + * Emits a server event + * @param command - The server command (e.g., "start", "stop", "register", "deregister") + * @param additionalProperties - Additional properties specific to the event + */ + private emitServerEvent(command: ServerCommand, commandDuration: number, error?: Error) { + const event: ServerEvent = { + timestamp: new Date().toISOString(), + source: "mdbmcp", + properties: { + result: "success", + duration_ms: commandDuration, + component: "server", + category: "other", + command: command, + }, + }; + + if (command === "start") { + event.properties.startup_time_ms = commandDuration; + event.properties.read_only_mode = this.userConfig.readOnly || false; + event.properties.disabled_tools = this.userConfig.disabledTools || []; + } + if (command === "stop") { + event.properties.runtime_duration_ms = Date.now() - this.startTime; + if (error) { + event.properties.result = "failure"; + event.properties.reason = error.message; + } + } + + this.telemetry.emitEvents([event]); + } + private registerTools() { for (const tool of [...AtlasTools, ...MongoDbTools]) { new tool(this.session, this.userConfig, this.telemetry).register(this.mcpServer); @@ -61,24 +140,67 @@ export class Server { } private registerResources() { + this.mcpServer.resource( + "config", + "config://config", + { + description: + "Server configuration, supplied by the user either as environment variables or as startup arguments", + }, + (uri) => { + const result = { + telemetry: this.userConfig.telemetry, + logPath: this.userConfig.logPath, + connectionString: this.userConfig.connectionString + ? "set; access to MongoDB tools are currently available to use" + : "not set; before using any MongoDB tool, you need to configure a connection string, alternatively you can setup MongoDB Atlas access, more info at 'https://github.com/mongodb-js/mongodb-mcp-server'.", + connectOptions: this.userConfig.connectOptions, + atlas: + this.userConfig.apiClientId && this.userConfig.apiClientSecret + ? "set; MongoDB Atlas tools are currently available to use" + : "not set; MongoDB Atlas tools are currently unavailable, to have access to MongoDB Atlas tools like creating clusters or connecting to clusters make sure to setup credentials, more info at 'https://github.com/mongodb-js/mongodb-mcp-server'.", + }; + return { + contents: [ + { + text: JSON.stringify(result), + mimeType: "application/json", + uri: uri.href, + }, + ], + }; + } + ); + } + + private async validateConfig(): Promise { if (this.userConfig.connectionString) { - this.mcpServer.resource( - "connection-string", - "config://connection-string", - { - description: "Preconfigured connection string that will be used as a default in the `connect` tool", - }, - (uri) => { - return { - contents: [ - { - text: `Preconfigured connection string: ${this.userConfig.connectionString}`, - uri: uri.href, - }, - ], - }; + try { + await this.session.connectToMongoDB(this.userConfig.connectionString, this.userConfig.connectOptions); + } 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"); + } + } + + if (this.userConfig.apiClientId && this.userConfig.apiClientSecret) { + try { + await this.session.apiClient.validateAccessToken(); + } catch (error) { + if (this.userConfig.connectionString === undefined) { + console.error("Failed to validate MongoDB Atlas the credentials from the config: ", error); + + throw new Error( + "Failed to connect to MongoDB Atlas instance using the credentials from the config" + ); } - ); + console.error( + "Failed to validate MongoDB Atlas the credentials from the config, but validated the connection string." + ); + } } } } diff --git a/src/session.ts b/src/session.ts index 2c5267ce..0b23883b 100644 --- a/src/session.ts +++ b/src/session.ts @@ -1,14 +1,22 @@ import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; import { ApiClient, ApiClientCredentials } from "./common/atlas/apiClient.js"; import { Implementation } from "@modelcontextprotocol/sdk/types.js"; +import logger, { LogId } from "./logger.js"; +import EventEmitter from "events"; +import { ConnectOptions } from "./config.js"; +import { setAppNameParamIfMissing } from "./helpers/connectionOptions.js"; +import { packageInfo } from "./helpers/packageInfo.js"; export interface SessionOptions { - apiBaseUrl?: string; + apiBaseUrl: string; apiClientId?: string; apiClientSecret?: string; } -export class Session { +export class Session extends EventEmitter<{ + close: []; + disconnect: []; +}> { sessionId?: string; serviceProvider?: NodeDriverServiceProvider; apiClient: ApiClient; @@ -16,8 +24,16 @@ export class Session { name: string; version: string; }; + connectedAtlasCluster?: { + username: string; + projectId: string; + clusterName: string; + expiryDate: Date; + }; + + constructor({ apiBaseUrl, apiClientId, apiClientSecret }: SessionOptions) { + super(); - constructor({ apiBaseUrl, apiClientId, apiClientSecret }: SessionOptions = {}) { const credentials: ApiClientCredentials | undefined = apiClientId && apiClientSecret ? { @@ -41,14 +57,64 @@ export class Session { } } - async close(): Promise { + async disconnect(): Promise { if (this.serviceProvider) { try { await this.serviceProvider.close(true); - } catch (error) { - console.error("Error closing service provider:", error); + } catch (err: unknown) { + const error = err instanceof Error ? err : new Error(String(err)); + logger.error(LogId.mongodbDisconnectFailure, "Error closing service provider:", error.message); } this.serviceProvider = undefined; } + if (!this.connectedAtlasCluster) { + this.emit("disconnect"); + return; + } + void this.apiClient + .deleteDatabaseUser({ + params: { + path: { + groupId: this.connectedAtlasCluster.projectId, + username: this.connectedAtlasCluster.username, + databaseName: "admin", + }, + }, + }) + .catch((err: unknown) => { + const error = err instanceof Error ? err : new Error(String(err)); + logger.error( + LogId.atlasDeleteDatabaseUserFailure, + "atlas-connect-cluster", + `Error deleting previous database user: ${error.message}` + ); + }); + this.connectedAtlasCluster = undefined; + + this.emit("disconnect"); + } + + async close(): Promise { + await this.disconnect(); + this.emit("close"); + } + + async connectToMongoDB(connectionString: string, connectOptions: ConnectOptions): Promise { + connectionString = setAppNameParamIfMissing({ + connectionString, + defaultAppName: `${packageInfo.mcpServerName} ${packageInfo.version}`, + }); + this.serviceProvider = await NodeDriverServiceProvider.connect(connectionString, { + productDocsLink: "https://github.com/mongodb-js/mongodb-mcp-server/", + productName: "MongoDB MCP", + readConcern: { + level: connectOptions.readConcern, + }, + readPreference: connectOptions.readPreference, + writeConcern: { + w: connectOptions.writeConcern, + }, + timeoutMS: connectOptions.timeoutMS, + }); } } diff --git a/src/telemetry/constants.ts b/src/telemetry/constants.ts index dfccbe75..9dd1cc76 100644 --- a/src/telemetry/constants.ts +++ b/src/telemetry/constants.ts @@ -1,11 +1,10 @@ -import { getMachineIdSync } from "native-machine-id"; -import { packageInfo } from "../packageInfo.js"; +import { packageInfo } from "../helpers/packageInfo.js"; +import { type CommonStaticProperties } from "./types.js"; /** * Machine-specific metadata formatted for telemetry */ -export const MACHINE_METADATA = { - device_id: getMachineIdSync(), +export const MACHINE_METADATA: CommonStaticProperties = { mcp_server_version: packageInfo.version, mcp_server_name: packageInfo.mcpServerName, platform: process.platform, diff --git a/src/telemetry/eventCache.ts b/src/telemetry/eventCache.ts index 49025227..26fc1f82 100644 --- a/src/telemetry/eventCache.ts +++ b/src/telemetry/eventCache.ts @@ -1,5 +1,5 @@ -import { BaseEvent } from "./types.js"; import { LRUCache } from "lru-cache"; +import { BaseEvent } from "./types.js"; /** * Singleton class for in-memory telemetry event caching @@ -13,7 +13,7 @@ export class EventCache { private cache: LRUCache; private nextId = 0; - private constructor() { + constructor() { this.cache = new LRUCache({ max: EventCache.MAX_EVENTS, // Using FIFO eviction strategy for events diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index a43b11c9..3f543341 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -1,138 +1,246 @@ import { Session } from "../session.js"; -import { BaseEvent } from "./types.js"; -import { config } from "../config.js"; -import logger from "../logger.js"; -import { mongoLogId } from "mongodb-log-writer"; +import { BaseEvent, CommonProperties } from "./types.js"; +import { UserConfig } from "../config.js"; +import logger, { LogId } from "../logger.js"; import { ApiClient } from "../common/atlas/apiClient.js"; import { MACHINE_METADATA } from "./constants.js"; import { EventCache } from "./eventCache.js"; +import nodeMachineId from "node-machine-id"; +import { getDeviceId } from "@mongodb-js/device-id"; +import fs from "fs/promises"; -type EventResult = { - success: boolean; - error?: Error; -}; - -type CommonProperties = { - device_id: string; - mcp_server_version: string; - mcp_server_name: string; - mcp_client_version?: string; - mcp_client_name?: string; - platform: string; - arch: string; - os_type: string; - os_version?: string; - session_id?: string; -}; +async function fileExists(filePath: string): Promise { + try { + await fs.access(filePath, fs.constants.F_OK); + return true; // File exists + } catch (e: unknown) { + if ( + e instanceof Error && + ( + e as Error & { + code: string; + } + ).code === "ENOENT" + ) { + return false; // File does not exist + } + throw e; // Re-throw unexpected errors + } +} + +async function isContainerized(): Promise { + if (process.env.container) { + return true; + } + + const exists = await Promise.all(["/.dockerenv", "/run/.containerenv", "/var/run/.containerenv"].map(fileExists)); + + return exists.includes(true); +} export class Telemetry { - private readonly commonProperties: CommonProperties; + private deviceIdAbortController = new AbortController(); + private eventCache: EventCache; + private getRawMachineId: () => Promise; + private getContainerEnv: () => Promise; + private cachedCommonProperties?: CommonProperties; + private flushing: boolean = false; - constructor( + private constructor( private readonly session: Session, - private readonly eventCache: EventCache = EventCache.getInstance() + private readonly userConfig: UserConfig, + { + eventCache, + getRawMachineId, + getContainerEnv, + }: { + eventCache: EventCache; + getRawMachineId: () => Promise; + getContainerEnv: () => Promise; + } ) { - this.commonProperties = { - ...MACHINE_METADATA, - }; + this.eventCache = eventCache; + this.getRawMachineId = getRawMachineId; + this.getContainerEnv = getContainerEnv; } - /** - * Checks if telemetry is currently enabled - * This is a method rather than a constant to capture runtime config changes - * - * Follows the Console Do Not Track standard (https://consoledonottrack.com/) - * by respecting the DO_NOT_TRACK environment variable - */ - private static isTelemetryEnabled(): boolean { - // Check if telemetry is explicitly disabled in config - if (config.telemetry === "disabled") { - return false; - } + static create( + session: Session, + userConfig: UserConfig, + { + eventCache = EventCache.getInstance(), + getRawMachineId = () => nodeMachineId.machineId(true), + getContainerEnv = isContainerized, + }: { + eventCache?: EventCache; + getRawMachineId?: () => Promise; + getContainerEnv?: () => Promise; + } = {} + ): Telemetry { + const instance = new Telemetry(session, userConfig, { + eventCache, + getRawMachineId, + getContainerEnv, + }); - const doNotTrack = process.env.DO_NOT_TRACK; - if (doNotTrack) { - const value = doNotTrack.toLowerCase(); - // Telemetry should be disabled if DO_NOT_TRACK is "1", "true", or "yes" - if (value === "1" || value === "true" || value === "yes") { - return false; - } - } + return instance; + } - return true; + public async close(): Promise { + this.deviceIdAbortController.abort(); + await this.flush(); } /** * Emits events through the telemetry pipeline * @param events - The events to emit */ - public async emitEvents(events: BaseEvent[]): Promise { - try { - if (!Telemetry.isTelemetryEnabled()) { - logger.debug(mongoLogId(1_000_000), "telemetry", "Telemetry is disabled, skipping events."); - return; - } - - await this.emit(events); - } catch { - logger.debug(mongoLogId(1_000_002), "telemetry", `Error emitting telemetry events.`); - } + public emitEvents(events: BaseEvent[]): void { + void this.flush(events); } /** * Gets the common properties for events * @returns Object containing common properties for all events */ - public getCommonProperties(): CommonProperties { - return { - ...this.commonProperties, - mcp_client_version: this.session.agentRunner?.version, - mcp_client_name: this.session.agentRunner?.name, - session_id: this.session.sessionId, - }; + private async getCommonProperties(): Promise { + if (!this.cachedCommonProperties) { + let deviceId: string | undefined; + let containerEnv: boolean | undefined; + try { + await Promise.all([ + getDeviceId({ + getMachineId: () => this.getRawMachineId(), + onError: (reason, error) => { + switch (reason) { + case "resolutionError": + logger.debug(LogId.telemetryDeviceIdFailure, "telemetry", String(error)); + break; + case "timeout": + logger.debug( + LogId.telemetryDeviceIdTimeout, + "telemetry", + "Device ID retrieval timed out" + ); + break; + case "abort": + // No need to log in the case of aborts + break; + } + }, + abortSignal: this.deviceIdAbortController.signal, + }).then((id) => { + deviceId = id; + }), + this.getContainerEnv().then((env) => { + containerEnv = env; + }), + ]); + } catch (error: unknown) { + const err = error instanceof Error ? error : new Error(String(error)); + logger.debug(LogId.telemetryDeviceIdFailure, "telemetry", err.message); + } + this.cachedCommonProperties = { + ...MACHINE_METADATA, + mcp_client_version: this.session.agentRunner?.version, + mcp_client_name: this.session.agentRunner?.name, + session_id: this.session.sessionId, + config_atlas_auth: this.session.apiClient.hasCredentials() ? "true" : "false", + config_connection_string: this.userConfig.connectionString ? "true" : "false", + is_container_env: containerEnv ? "true" : "false", + device_id: deviceId, + }; + } + + return this.cachedCommonProperties; } /** - * Attempts to emit events through authenticated and unauthenticated clients + * Checks if telemetry is currently enabled + * This is a method rather than a constant to capture runtime config changes + * + * Follows the Console Do Not Track standard (https://consoledonottrack.com/) + * by respecting the DO_NOT_TRACK environment variable + */ + public isTelemetryEnabled(): boolean { + // Check if telemetry is explicitly disabled in config + if (this.userConfig.telemetry === "disabled") { + return false; + } + + const doNotTrack = "DO_NOT_TRACK" in process.env; + return !doNotTrack; + } + + /** + * Attempts to flush events through authenticated and unauthenticated clients * Falls back to caching if both attempts fail */ - private async emit(events: BaseEvent[]): Promise { - const cachedEvents = this.eventCache.getEvents(); - const allEvents = [...cachedEvents, ...events]; - - logger.debug( - mongoLogId(1_000_003), - "telemetry", - `Attempting to send ${allEvents.length} events (${cachedEvents.length} cached)` - ); + public async flush(events?: BaseEvent[]): Promise { + if (!this.isTelemetryEnabled()) { + logger.info(LogId.telemetryEmitFailure, "telemetry", `Telemetry is disabled.`); + return; + } - const result = await this.sendEvents(this.session.apiClient, allEvents); - if (result.success) { - this.eventCache.clearEvents(); - logger.debug(mongoLogId(1_000_004), "telemetry", `Sent ${allEvents.length} events successfully`); + if (this.flushing) { + this.eventCache.appendEvents(events ?? []); + process.nextTick(async () => { + // try again if in the middle of a flush + await this.flush(); + }); return; } - logger.warning( - mongoLogId(1_000_005), - "telemetry", - `Error sending event to client: ${result.error instanceof Error ? result.error.message : String(result.error)}` - ); - this.eventCache.appendEvents(events); + this.flushing = true; + + try { + const cachedEvents = this.eventCache.getEvents(); + const allEvents = [...cachedEvents, ...(events ?? [])]; + if (allEvents.length <= 0) { + this.flushing = false; + return; + } + + logger.debug( + LogId.telemetryEmitStart, + "telemetry", + `Attempting to send ${allEvents.length} events (${cachedEvents.length} cached)` + ); + + await this.sendEvents(this.session.apiClient, allEvents); + this.eventCache.clearEvents(); + logger.debug( + LogId.telemetryEmitSuccess, + "telemetry", + `Sent ${allEvents.length} events successfully: ${JSON.stringify(allEvents, null, 2)}` + ); + } catch (error: unknown) { + logger.debug( + LogId.telemetryEmitFailure, + "telemetry", + `Error sending event to client: ${error instanceof Error ? error.message : String(error)}` + ); + this.eventCache.appendEvents(events ?? []); + process.nextTick(async () => { + // try again + await this.flush(); + }); + } + + this.flushing = false; } /** * Attempts to send events through the provided API client */ - private async sendEvents(client: ApiClient, events: BaseEvent[]): Promise { - try { - await client.sendEvents(events); - return { success: true }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error : new Error(String(error)), - }; - } + private async sendEvents(client: ApiClient, events: BaseEvent[]): Promise { + const commonProperties = await this.getCommonProperties(); + + await client.sendEvents( + events.map((event) => ({ + ...event, + properties: { ...commonProperties, ...event.properties }, + })) + ); } } diff --git a/src/telemetry/types.ts b/src/telemetry/types.ts index 4f24e545..05ce8f3f 100644 --- a/src/telemetry/types.ts +++ b/src/telemetry/types.ts @@ -2,46 +2,74 @@ * Result type constants for telemetry events */ export type TelemetryResult = "success" | "failure"; +export type ServerCommand = "start" | "stop"; +export type TelemetryBoolSet = "true" | "false"; /** * Base interface for all events */ -export interface Event { +export type TelemetryEvent = { timestamp: string; source: "mdbmcp"; - properties: Record; -} - -export interface BaseEvent extends Event { - properties: { - device_id: string; - mcp_server_version: string; - mcp_server_name: string; - mcp_client_version?: string; - mcp_client_name?: string; - platform: string; - arch: string; - os_type: string; + properties: T & { component: string; duration_ms: number; result: TelemetryResult; category: string; - os_version?: string; - session_id?: string; - } & Event["properties"]; -} + }; +}; + +export type BaseEvent = TelemetryEvent; /** * Interface for tool events */ -export interface ToolEvent extends BaseEvent { - properties: { - command: string; - error_code?: string; - error_type?: string; - project_id?: string; - org_id?: string; - cluster_name?: string; - is_atlas?: boolean; - } & BaseEvent["properties"]; -} +export type ToolEventProperties = { + command: string; + error_code?: string; + error_type?: string; + project_id?: string; + org_id?: string; + cluster_name?: string; + is_atlas?: boolean; +}; + +export type ToolEvent = TelemetryEvent; +/** + * Interface for server events + */ +export type ServerEventProperties = { + command: ServerCommand; + reason?: string; + startup_time_ms?: number; + runtime_duration_ms?: number; + read_only_mode?: boolean; + disabled_tools?: string[]; +}; + +export type ServerEvent = TelemetryEvent; + +/** + * Interface for static properties, they can be fetched once and reused. + */ +export type CommonStaticProperties = { + mcp_server_version: string; + mcp_server_name: string; + platform: string; + arch: string; + os_type: string; + os_version?: string; +}; + +/** + * Common properties for all events that might change. + */ +export type CommonProperties = { + device_id?: string; + mcp_client_version?: string; + mcp_client_name?: string; + config_atlas_auth?: TelemetryBoolSet; + config_connection_string?: TelemetryBoolSet; + session_id?: string; + is_container_env?: TelemetryBoolSet; +} & CommonStaticProperties; diff --git a/src/tools/atlas/atlasTool.ts b/src/tools/atlas/atlasTool.ts index 6ca5282d..2b93a5ec 100644 --- a/src/tools/atlas/atlasTool.ts +++ b/src/tools/atlas/atlasTool.ts @@ -1,4 +1,9 @@ -import { ToolBase, ToolCategory } from "../tool.js"; +import { ToolBase, ToolCategory, TelemetryToolMetadata, ToolArgs } from "../tool.js"; +import { ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import logger, { LogId } from "../../logger.js"; +import { z } from "zod"; +import { ApiClientError } from "../../common/atlas/apiClientError.js"; export abstract class AtlasToolBase extends ToolBase { protected category: ToolCategory = "atlas"; @@ -9,4 +14,90 @@ export abstract class AtlasToolBase extends ToolBase { } return super.verifyAllowed(); } + + protected handleError( + error: unknown, + args: ToolArgs + ): Promise | CallToolResult { + if (error instanceof ApiClientError) { + const statusCode = error.response.status; + + if (statusCode === 401) { + return { + content: [ + { + type: "text", + text: `Unable to authenticate with MongoDB Atlas, API error: ${error.message} + +Hint: Your API credentials may be invalid, expired or lack permissions. +Please check your Atlas API credentials and ensure they have the appropriate permissions. +For more information on setting up API keys, visit: https://www.mongodb.com/docs/atlas/configure-api-access/`, + }, + ], + isError: true, + }; + } + + if (statusCode === 403) { + return { + content: [ + { + type: "text", + text: `Received a Forbidden API Error: ${error.message} + +You don't have sufficient permissions to perform this action in MongoDB Atlas +Please ensure your API key has the necessary roles assigned. +For more information on Atlas API access roles, visit: https://www.mongodb.com/docs/atlas/api/service-accounts-overview/`, + }, + ], + isError: true, + }; + } + } + + // For other types of errors, use the default error handling from the base class + return super.handleError(error, args); + } + + /** + * + * Resolves the tool metadata from the arguments passed to the tool + * + * @param args - The arguments passed to the tool + * @returns The tool metadata + */ + protected resolveTelemetryMetadata( + ...args: Parameters> + ): TelemetryToolMetadata { + const toolMetadata: TelemetryToolMetadata = {}; + if (!args.length) { + return toolMetadata; + } + + // Create a typed parser for the exact shape we expect + const argsShape = z.object(this.argsShape); + const parsedResult = argsShape.safeParse(args[0]); + + if (!parsedResult.success) { + logger.debug( + LogId.telemetryMetadataError, + "tool", + `Error parsing tool arguments: ${parsedResult.error.message}` + ); + return toolMetadata; + } + + const data = parsedResult.data; + + // Extract projectId using type guard + if ("projectId" in data && typeof data.projectId === "string" && data.projectId.trim() !== "") { + toolMetadata.projectId = data.projectId; + } + + // Extract orgId using type guard + if ("orgId" in data && typeof data.orgId === "string" && data.orgId.trim() !== "") { + toolMetadata.orgId = data.orgId; + } + return toolMetadata; + } } diff --git a/src/tools/atlas/createAccessList.ts b/src/tools/atlas/create/createAccessList.ts similarity index 96% rename from src/tools/atlas/createAccessList.ts rename to src/tools/atlas/create/createAccessList.ts index 46eb9af6..1c38279a 100644 --- a/src/tools/atlas/createAccessList.ts +++ b/src/tools/atlas/create/createAccessList.ts @@ -1,7 +1,7 @@ import { z } from "zod"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { ToolArgs, OperationType } from "../tool.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; const DEFAULT_COMMENT = "Added by Atlas MCP"; diff --git a/src/tools/atlas/createDBUser.ts b/src/tools/atlas/create/createDBUser.ts similarity index 61% rename from src/tools/atlas/createDBUser.ts rename to src/tools/atlas/create/createDBUser.ts index 0b0122c9..a8266a0a 100644 --- a/src/tools/atlas/createDBUser.ts +++ b/src/tools/atlas/create/createDBUser.ts @@ -1,8 +1,9 @@ import { z } from "zod"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { ToolArgs, OperationType } from "../tool.js"; -import { CloudDatabaseUser, DatabaseUserRole } from "../../common/atlas/openapi.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; +import { CloudDatabaseUser, DatabaseUserRole } from "../../../common/atlas/openapi.js"; +import { generateSecurePassword } from "../../../common/atlas/generatePassword.js"; export class CreateDBUserTool extends AtlasToolBase { protected name = "atlas-create-db-user"; @@ -11,7 +12,16 @@ export class CreateDBUserTool extends AtlasToolBase { protected argsShape = { projectId: z.string().describe("Atlas project ID"), username: z.string().describe("Username for the new user"), - password: z.string().describe("Password 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. 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({ @@ -34,6 +44,11 @@ export class CreateDBUserTool extends AtlasToolBase { roles, clusters, }: ToolArgs): Promise { + const shouldGeneratePassword = !password; + if (shouldGeneratePassword) { + password = await generateSecurePassword(); + } + const input = { groupId: projectId, awsIAMType: "NONE", @@ -62,7 +77,12 @@ export class CreateDBUserTool extends AtlasToolBase { }); return { - content: [{ type: "text", text: `User "${username}" created sucessfully.` }], + content: [ + { + type: "text", + text: `User "${username}" created successfully${shouldGeneratePassword ? ` with password: \`${password}\`` : ""}.`, + }, + ], }; } } diff --git a/src/tools/atlas/createFreeCluster.ts b/src/tools/atlas/create/createFreeCluster.ts similarity index 79% rename from src/tools/atlas/createFreeCluster.ts rename to src/tools/atlas/create/createFreeCluster.ts index ccf13856..2d93ae80 100644 --- a/src/tools/atlas/createFreeCluster.ts +++ b/src/tools/atlas/create/createFreeCluster.ts @@ -1,8 +1,8 @@ import { z } from "zod"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { ToolArgs, OperationType } from "../tool.js"; -import { ClusterDescription20240805 } from "../../common/atlas/openapi.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; +import { ClusterDescription20240805 } from "../../../common/atlas/openapi.js"; export class CreateFreeClusterTool extends AtlasToolBase { protected name = "atlas-create-free-cluster"; @@ -47,7 +47,10 @@ export class CreateFreeClusterTool extends AtlasToolBase { }); return { - content: [{ type: "text", text: `Cluster "${name}" has been created in region "${region}".` }], + content: [ + { type: "text", text: `Cluster "${name}" has been created in region "${region}".` }, + { type: "text", text: `Double check your access lists to enable your current IP.` }, + ], }; } } diff --git a/src/tools/atlas/createProject.ts b/src/tools/atlas/create/createProject.ts similarity index 80% rename from src/tools/atlas/createProject.ts rename to src/tools/atlas/create/createProject.ts index f1c4da31..cdf71b9c 100644 --- a/src/tools/atlas/createProject.ts +++ b/src/tools/atlas/create/createProject.ts @@ -1,8 +1,8 @@ import { z } from "zod"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { ToolArgs, OperationType } from "../tool.js"; -import { Group } from "../../common/atlas/openapi.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; +import { Group } from "../../../common/atlas/openapi.js"; export class CreateProjectTool extends AtlasToolBase { protected name = "atlas-create-project"; @@ -28,7 +28,13 @@ export class CreateProjectTool extends AtlasToolBase { "No organizations were found in your MongoDB Atlas account. Please create an organization first." ); } - organizationId = organizations.results[0].id; + const firstOrg = organizations.results[0]; + if (!firstOrg?.id) { + throw new Error( + "The first organization found does not have an ID. Please check your Atlas account." + ); + } + organizationId = firstOrg.id; assumedOrg = true; } catch { throw new Error( diff --git a/src/tools/atlas/metadata/connectCluster.ts b/src/tools/atlas/metadata/connectCluster.ts new file mode 100644 index 00000000..18970e24 --- /dev/null +++ b/src/tools/atlas/metadata/connectCluster.ts @@ -0,0 +1,121 @@ +import { z } from "zod"; +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; +import { generateSecurePassword } from "../../../common/atlas/generatePassword.js"; +import logger, { LogId } from "../../../logger.js"; +import { inspectCluster } from "../../../common/atlas/cluster.js"; + +const EXPIRY_MS = 1000 * 60 * 60 * 12; // 12 hours + +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} +export class ConnectClusterTool extends AtlasToolBase { + protected name = "atlas-connect-cluster"; + protected description = "Connect to MongoDB Atlas cluster"; + protected operationType: OperationType = "metadata"; + protected argsShape = { + projectId: z.string().describe("Atlas project ID"), + clusterName: z.string().describe("Atlas cluster name"), + }; + + protected async execute({ projectId, clusterName }: ToolArgs): Promise { + await this.session.disconnect(); + + const cluster = await inspectCluster(this.session.apiClient, projectId, clusterName); + + if (!cluster.connectionString) { + throw new Error("Connection string not available"); + } + + const username = `mcpUser${Math.floor(Math.random() * 100000)}`; + const password = await generateSecurePassword(); + + const expiryDate = new Date(Date.now() + EXPIRY_MS); + + const readOnly = + this.config.readOnly || + (this.config.disabledTools?.includes("create") && + this.config.disabledTools?.includes("update") && + this.config.disabledTools?.includes("delete") && + !this.config.disabledTools?.includes("read") && + !this.config.disabledTools?.includes("metadata")); + + const roleName = readOnly ? "readAnyDatabase" : "readWriteAnyDatabase"; + + await this.session.apiClient.createDatabaseUser({ + params: { + path: { + groupId: projectId, + }, + }, + body: { + databaseName: "admin", + groupId: projectId, + roles: [ + { + roleName, + databaseName: "admin", + }, + ], + scopes: [{ type: "CLUSTER", name: clusterName }], + username, + password, + awsIAMType: "NONE", + ldapAuthType: "NONE", + oidcAuthType: "NONE", + x509Type: "NONE", + deleteAfterDate: expiryDate.toISOString(), + }, + }); + + this.session.connectedAtlasCluster = { + username, + projectId, + clusterName, + expiryDate, + }; + + const cn = new URL(cluster.connectionString); + cn.username = username; + cn.password = password; + cn.searchParams.set("authSource", "admin"); + const connectionString = cn.toString(); + + let lastError: Error | undefined = undefined; + + for (let i = 0; i < 20; i++) { + try { + await this.session.connectToMongoDB(connectionString, this.config.connectOptions); + lastError = undefined; + break; + } catch (err: unknown) { + const error = err instanceof Error ? err : new Error(String(err)); + + lastError = error; + + logger.debug( + LogId.atlasConnectFailure, + "atlas-connect-cluster", + `error connecting to cluster: ${error.message}` + ); + + await sleep(500); // wait for 500ms before retrying + } + } + + if (lastError) { + throw lastError; + } + + return { + content: [ + { + type: "text", + text: `Connected to cluster "${clusterName}"`, + }, + ], + }; + } +} diff --git a/src/tools/atlas/inspectAccessList.ts b/src/tools/atlas/read/inspectAccessList.ts similarity index 92% rename from src/tools/atlas/inspectAccessList.ts rename to src/tools/atlas/read/inspectAccessList.ts index 755da768..94c85228 100644 --- a/src/tools/atlas/inspectAccessList.ts +++ b/src/tools/atlas/read/inspectAccessList.ts @@ -1,7 +1,7 @@ import { z } from "zod"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { ToolArgs, OperationType } from "../tool.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; export class InspectAccessListTool extends AtlasToolBase { protected name = "atlas-inspect-access-list"; diff --git a/src/tools/atlas/inspectCluster.ts b/src/tools/atlas/read/inspectCluster.ts similarity index 51% rename from src/tools/atlas/inspectCluster.ts rename to src/tools/atlas/read/inspectCluster.ts index c435beaf..c73c1b76 100644 --- a/src/tools/atlas/inspectCluster.ts +++ b/src/tools/atlas/read/inspectCluster.ts @@ -1,8 +1,8 @@ import { z } from "zod"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { ToolArgs, OperationType } from "../tool.js"; -import { ClusterDescription20240805 } from "../../common/atlas/openapi.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; +import { Cluster, inspectCluster } from "../../../common/atlas/cluster.js"; export class InspectClusterTool extends AtlasToolBase { protected name = "atlas-inspect-cluster"; @@ -14,30 +14,19 @@ export class InspectClusterTool extends AtlasToolBase { }; protected async execute({ projectId, clusterName }: ToolArgs): Promise { - const cluster = await this.session.apiClient.getCluster({ - params: { - path: { - groupId: projectId, - clusterName, - }, - }, - }); + const cluster = await inspectCluster(this.session.apiClient, projectId, clusterName); return this.formatOutput(cluster); } - private formatOutput(cluster?: ClusterDescription20240805): CallToolResult { - if (!cluster) { - throw new Error("Cluster not found"); - } - + private formatOutput(formattedCluster: Cluster): CallToolResult { return { content: [ { type: "text", - text: `Cluster Name | State | MongoDB Version | Connection String -----------------|----------------|----------------|----------------|---------------- -${cluster.name} | ${cluster.stateName} | ${cluster.mongoDBVersion || "N/A"} | ${cluster.connectionStrings?.standard || "N/A"}`, + text: `Cluster Name | Cluster Type | Tier | State | MongoDB Version | Connection String +----------------|----------------|----------------|----------------|----------------|---------------- +${formattedCluster.name || "Unknown"} | ${formattedCluster.instanceType} | ${formattedCluster.instanceSize || "N/A"} | ${formattedCluster.state || "UNKNOWN"} | ${formattedCluster.mongoDBVersion || "N/A"} | ${formattedCluster.connectionString || "N/A"}`, }, ], }; diff --git a/src/tools/atlas/read/listAlerts.ts b/src/tools/atlas/read/listAlerts.ts new file mode 100644 index 00000000..bbbf6f14 --- /dev/null +++ b/src/tools/atlas/read/listAlerts.ts @@ -0,0 +1,45 @@ +import { z } from "zod"; +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; + +export class ListAlertsTool extends AtlasToolBase { + protected name = "atlas-list-alerts"; + protected description = "List MongoDB Atlas alerts"; + protected operationType: OperationType = "read"; + protected argsShape = { + projectId: z.string().describe("Atlas project ID to list alerts for"), + }; + + protected async execute({ projectId }: ToolArgs): Promise { + const data = await this.session.apiClient.listAlerts({ + params: { + path: { + groupId: projectId, + }, + }, + }); + + if (!data?.results?.length) { + return { content: [{ type: "text", text: "No alerts found in your MongoDB Atlas project." }] }; + } + + // Format alerts as a table + const output = + `Alert ID | Status | Created | Updated | Type | Comment +----------|---------|----------|----------|------|-------- +` + + data.results + .map((alert) => { + const created = alert.created ? new Date(alert.created).toLocaleString() : "N/A"; + const updated = alert.updated ? new Date(alert.updated).toLocaleString() : "N/A"; + const comment = alert.acknowledgementComment ?? "N/A"; + return `${alert.id} | ${alert.status} | ${created} | ${updated} | ${alert.eventTypeName} | ${comment}`; + }) + .join("\n"); + + return { + content: [{ type: "text", text: output }], + }; + } +} diff --git a/src/tools/atlas/listClusters.ts b/src/tools/atlas/read/listClusters.ts similarity index 66% rename from src/tools/atlas/listClusters.ts rename to src/tools/atlas/read/listClusters.ts index 82a59fd4..a8af8828 100644 --- a/src/tools/atlas/listClusters.ts +++ b/src/tools/atlas/read/listClusters.ts @@ -1,8 +1,14 @@ import { z } from "zod"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { ToolArgs, OperationType } from "../tool.js"; -import { PaginatedClusterDescription20240805, PaginatedOrgGroupView, Group } from "../../common/atlas/openapi.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; +import { + PaginatedClusterDescription20240805, + PaginatedOrgGroupView, + Group, + PaginatedFlexClusters20241113, +} from "../../../common/atlas/openapi.js"; +import { formatCluster, formatFlexCluster } from "../../../common/atlas/cluster.js"; export class ListClustersTool extends AtlasToolBase { protected name = "atlas-list-clusters"; @@ -73,15 +79,20 @@ ${rows}`, }; } - private formatClustersTable(project: Group, clusters?: PaginatedClusterDescription20240805): CallToolResult { - if (!clusters?.results?.length) { + private formatClustersTable( + project: Group, + clusters?: PaginatedClusterDescription20240805, + flexClusters?: PaginatedFlexClusters20241113 + ): CallToolResult { + // Check if both traditional clusters and flex clusters are absent + if (!clusters?.results?.length && !flexClusters?.results?.length) { throw new Error("No clusters found."); } - const rows = clusters.results - .map((cluster) => { - const connectionString = cluster.connectionStrings?.standard || "N/A"; - const mongoDBVersion = cluster.mongoDBVersion || "N/A"; - return `${cluster.name} | ${cluster.stateName} | ${mongoDBVersion} | ${connectionString}`; + 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"); return { @@ -92,8 +103,8 @@ ${rows}`, }, { type: "text", - text: `Cluster Name | State | MongoDB Version | Connection String -----------------|----------------|----------------|----------------|---------------- + text: `Cluster Name | Cluster Type | Tier | State | MongoDB Version | Connection String +----------------|----------------|----------------|----------------|----------------|---------------- ${rows}`, }, ], diff --git a/src/tools/atlas/listDBUsers.ts b/src/tools/atlas/read/listDBUsers.ts similarity index 90% rename from src/tools/atlas/listDBUsers.ts rename to src/tools/atlas/read/listDBUsers.ts index c3013162..7650cbf0 100644 --- a/src/tools/atlas/listDBUsers.ts +++ b/src/tools/atlas/read/listDBUsers.ts @@ -1,8 +1,8 @@ import { z } from "zod"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { ToolArgs, OperationType } from "../tool.js"; -import { DatabaseUserRole, UserScope } from "../../common/atlas/openapi.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; +import { DatabaseUserRole, UserScope } from "../../../common/atlas/openapi.js"; export class ListDBUsersTool extends AtlasToolBase { protected name = "atlas-list-db-users"; diff --git a/src/tools/atlas/listOrgs.ts b/src/tools/atlas/read/listOrgs.ts similarity index 91% rename from src/tools/atlas/listOrgs.ts rename to src/tools/atlas/read/listOrgs.ts index 2bfa95c2..c55738d7 100644 --- a/src/tools/atlas/listOrgs.ts +++ b/src/tools/atlas/read/listOrgs.ts @@ -1,6 +1,6 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { OperationType } from "../tool.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { OperationType } from "../../tool.js"; export class ListOrganizationsTool extends AtlasToolBase { protected name = "atlas-list-orgs"; diff --git a/src/tools/atlas/listProjects.ts b/src/tools/atlas/read/listProjects.ts similarity index 58% rename from src/tools/atlas/listProjects.ts rename to src/tools/atlas/read/listProjects.ts index be127b29..1a9ab523 100644 --- a/src/tools/atlas/listProjects.ts +++ b/src/tools/atlas/read/listProjects.ts @@ -1,8 +1,8 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { AtlasToolBase } from "./atlasTool.js"; -import { OperationType } from "../tool.js"; +import { AtlasToolBase } from "../atlasTool.js"; +import { OperationType } from "../../tool.js"; import { z } from "zod"; -import { ToolArgs } from "../tool.js"; +import { ToolArgs } from "../../tool.js"; export class ListProjectsTool extends AtlasToolBase { protected name = "atlas-list-projects"; @@ -13,6 +13,17 @@ export class ListProjectsTool extends AtlasToolBase { }; protected async execute({ orgId }: ToolArgs): Promise { + const orgData = await this.session.apiClient.listOrganizations(); + + if (!orgData?.results?.length) { + throw new Error("No organizations found in your MongoDB Atlas account."); + } + + const orgs: Record = orgData.results + .map((org) => [org.id || "", org.name]) + .filter(([id]) => id) + .reduce((acc, [id, name]) => ({ ...acc, [id as string]: name }), {}); + const data = orgId ? await this.session.apiClient.listOrganizationProjects({ params: { @@ -31,11 +42,12 @@ export class ListProjectsTool extends AtlasToolBase { const rows = data.results .map((project) => { const createdAt = project.created ? new Date(project.created).toLocaleString() : "N/A"; - return `${project.name} | ${project.id} | ${createdAt}`; + const orgName = orgs[project.orgId] ?? "N/A"; + return `${project.name} | ${project.id} | ${orgName} | ${project.orgId} | ${createdAt}`; }) .join("\n"); - const formattedProjects = `Project Name | Project ID | Created At -----------------| ----------------| ---------------- + const formattedProjects = `Project Name | Project ID | Organization Name | Organization ID | Created At +----------------| ----------------| ----------------| ----------------| ---------------- ${rows}`; return { content: [{ type: "text", text: formattedProjects }], diff --git a/src/tools/atlas/tools.ts b/src/tools/atlas/tools.ts index 4a3772ef..9c27740d 100644 --- a/src/tools/atlas/tools.ts +++ b/src/tools/atlas/tools.ts @@ -1,13 +1,15 @@ -import { ListClustersTool } from "./listClusters.js"; -import { ListProjectsTool } from "./listProjects.js"; -import { InspectClusterTool } from "./inspectCluster.js"; -import { CreateFreeClusterTool } from "./createFreeCluster.js"; -import { CreateAccessListTool } from "./createAccessList.js"; -import { InspectAccessListTool } from "./inspectAccessList.js"; -import { ListDBUsersTool } from "./listDBUsers.js"; -import { CreateDBUserTool } from "./createDBUser.js"; -import { CreateProjectTool } from "./createProject.js"; -import { ListOrganizationsTool } from "./listOrgs.js"; +import { ListClustersTool } from "./read/listClusters.js"; +import { ListProjectsTool } from "./read/listProjects.js"; +import { InspectClusterTool } from "./read/inspectCluster.js"; +import { CreateFreeClusterTool } from "./create/createFreeCluster.js"; +import { CreateAccessListTool } from "./create/createAccessList.js"; +import { InspectAccessListTool } from "./read/inspectAccessList.js"; +import { ListDBUsersTool } from "./read/listDBUsers.js"; +import { CreateDBUserTool } from "./create/createDBUser.js"; +import { CreateProjectTool } from "./create/createProject.js"; +import { ListOrganizationsTool } from "./read/listOrgs.js"; +import { ConnectClusterTool } from "./metadata/connectCluster.js"; +import { ListAlertsTool } from "./read/listAlerts.js"; export const AtlasTools = [ ListClustersTool, @@ -20,4 +22,6 @@ export const AtlasTools = [ CreateDBUserTool, CreateProjectTool, ListOrganizationsTool, + ConnectClusterTool, + ListAlertsTool, ]; diff --git a/src/tools/mongodb/create/insertMany.ts b/src/tools/mongodb/create/insertMany.ts index eb624275..f28d79d5 100644 --- a/src/tools/mongodb/create/insertMany.ts +++ b/src/tools/mongodb/create/insertMany.ts @@ -9,7 +9,7 @@ export class InsertManyTool extends MongoDBToolBase { protected argsShape = { ...DbOperationArgs, documents: z - .array(z.object({}).passthrough().describe("An individual MongoDB document")) + .array(z.record(z.string(), z.unknown()).describe("An individual MongoDB document")) .describe( "The array of documents to insert, matching the syntax of the document argument of db.collection.insertMany()" ), diff --git a/src/tools/mongodb/delete/deleteMany.ts b/src/tools/mongodb/delete/deleteMany.ts index 9e6e9fde..6b8351ef 100644 --- a/src/tools/mongodb/delete/deleteMany.ts +++ b/src/tools/mongodb/delete/deleteMany.ts @@ -9,8 +9,7 @@ export class DeleteManyTool extends MongoDBToolBase { protected argsShape = { ...DbOperationArgs, filter: z - .object({}) - .passthrough() + .record(z.string(), z.unknown()) .optional() .describe( "The query filter, specifying the deletion criteria. Matches the syntax of the filter argument of db.collection.deleteMany()" diff --git a/src/tools/mongodb/metadata/connect.ts b/src/tools/mongodb/metadata/connect.ts index 746da9b3..defbf47f 100644 --- a/src/tools/mongodb/metadata/connect.ts +++ b/src/tools/mongodb/metadata/connect.ts @@ -2,92 +2,95 @@ import { z } from "zod"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { MongoDBToolBase } from "../mongodbTool.js"; import { ToolArgs, OperationType } from "../../tool.js"; -import { MongoError as DriverError } from "mongodb"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import assert from "assert"; +import { UserConfig } from "../../../config.js"; +import { Telemetry } from "../../../telemetry/telemetry.js"; +import { Session } from "../../../session.js"; + +const disconnectedSchema = z + .object({ + connectionString: z.string().describe("MongoDB connection string (in the mongodb:// or mongodb+srv:// format)"), + }) + .describe("Options for connecting to MongoDB."); + +const connectedSchema = z + .object({ + connectionString: z + .string() + .optional() + .describe("MongoDB connection string to switch to (in the mongodb:// or mongodb+srv:// format)"), + }) + .describe( + "Options for switching the current MongoDB connection. If a connection string is not provided, the connection string from the config will be used." + ); + +const connectedName = "switch-connection" as const; +const disconnectedName = "connect" as const; + +const connectedDescription = + "Switch to a different MongoDB connection. If the user has configured a connection string or has previously called the connect tool, a connection is already established and there's no need to call this tool unless the user has explicitly requested to switch to a new instance."; +const disconnectedDescription = "Connect to a MongoDB instance"; export class ConnectTool extends MongoDBToolBase { - protected name = "connect"; - protected description = "Connect to a MongoDB instance"; + protected name: typeof connectedName | typeof disconnectedName = disconnectedName; + protected description: typeof connectedDescription | typeof disconnectedDescription = disconnectedDescription; + + // Here the default is empty just to trigger registration, but we're going to override it with the correct + // schema in the register method. protected argsShape = { - options: z - .array( - z - .union([ - z.object({ - connectionString: z - .string() - .describe("MongoDB connection string (in the mongodb:// or mongodb+srv:// format)"), - }), - z.object({ - clusterName: z.string().describe("MongoDB cluster name"), - }), - ]) - .optional() - ) - .optional() - .describe( - "Options for connecting to MongoDB. If not provided, the connection string from the config://connection-string resource will be used. If the user hasn't specified Atlas cluster name or a connection string explicitly and the `config://connection-string` resource is present, always invoke this with no arguments." - ), + connectionString: z.string().optional(), }; protected operationType: OperationType = "metadata"; - protected async execute({ options: optionsArr }: ToolArgs): Promise { - const options = optionsArr?.[0]; - let connectionString: string; - if (!options && !this.config.connectionString) { - return { - content: [ - { type: "text", text: "No connection details provided." }, - { type: "text", text: "Please provide either a connection string or a cluster name" }, - ], - }; - } + constructor(session: Session, config: UserConfig, telemetry: Telemetry) { + super(session, config, telemetry); + session.on("close", () => { + this.updateMetadata(); + }); + } - if (!options) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - connectionString = this.config.connectionString!; - } else if ("connectionString" in options) { - connectionString = options.connectionString; - } else { - // TODO: https://github.com/mongodb-js/mongodb-mcp-server/issues/19 - // We don't support connecting via cluster name since we'd need to obtain the user credentials - // and fill in the connection string. - return { - content: [ - { - type: "text", - text: `Connecting via cluster name not supported yet. Please provide a connection string.`, - }, - ], - }; + protected async execute({ connectionString }: ToolArgs): Promise { + switch (this.name) { + case disconnectedName: + assert(connectionString, "Connection string is required"); + break; + case connectedName: + connectionString ??= this.config.connectionString; + assert( + connectionString, + "Cannot switch to a new connection because no connection string was provided and no default connection string is configured." + ); + break; } - try { - await this.connectToMongoDB(connectionString); - return { - content: [{ type: "text", text: `Successfully connected to ${connectionString}.` }], - }; - } catch (error) { - // Sometimes the model will supply an incorrect connection string. If the user has configured - // a different one as environment variable or a cli argument, suggest using that one instead. - if ( - this.config.connectionString && - error instanceof DriverError && - this.config.connectionString !== connectionString - ) { - return { - content: [ - { - type: "text", - text: - `Failed to connect to MongoDB at '${connectionString}' due to error: '${error.message}.` + - `Your config lists a different connection string: '${this.config.connectionString}' - do you want to try connecting to it instead?`, - }, - ], - }; - } + await this.connectToMongoDB(connectionString); + this.updateMetadata(); + return { + content: [{ type: "text", text: "Successfully connected to MongoDB." }], + }; + } + + public register(server: McpServer): void { + super.register(server); - throw error; + this.updateMetadata(); + } + + private updateMetadata(): void { + if (this.config.connectionString || this.session.serviceProvider) { + this.update?.({ + name: connectedName, + description: connectedDescription, + inputSchema: connectedSchema, + }); + } else { + this.update?.({ + name: disconnectedName, + description: disconnectedDescription, + inputSchema: disconnectedSchema, + }); } } } diff --git a/src/tools/mongodb/metadata/listDatabases.ts b/src/tools/mongodb/metadata/listDatabases.ts index ce943a69..fe324f07 100644 --- a/src/tools/mongodb/metadata/listDatabases.ts +++ b/src/tools/mongodb/metadata/listDatabases.ts @@ -7,7 +7,6 @@ export class ListDatabasesTool extends MongoDBToolBase { protected name = "list-databases"; protected description = "List all databases for a MongoDB connection"; protected argsShape = {}; - protected operationType: OperationType = "metadata"; protected async execute(): Promise { diff --git a/src/tools/mongodb/metadata/logs.ts b/src/tools/mongodb/metadata/logs.ts new file mode 100644 index 00000000..9056aa59 --- /dev/null +++ b/src/tools/mongodb/metadata/logs.ts @@ -0,0 +1,55 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { MongoDBToolBase } from "../mongodbTool.js"; +import { ToolArgs, OperationType } from "../../tool.js"; +import { z } from "zod"; + +export class LogsTool extends MongoDBToolBase { + protected name = "mongodb-logs"; + protected description = "Returns the most recent logged mongod events"; + protected argsShape = { + type: z + .enum(["global", "startupWarnings"]) + .optional() + .default("global") + .describe( + "The type of logs to return. Global returns all recent log entries, while startupWarnings returns only warnings and errors from when the process started." + ), + limit: z + .number() + .int() + .max(1024) + .min(1) + .optional() + .default(50) + .describe("The maximum number of log entries to return."), + }; + + protected operationType: OperationType = "metadata"; + + protected async execute({ type, limit }: ToolArgs): Promise { + const provider = await this.ensureConnected(); + + const result = await provider.runCommandWithCheck("admin", { + getLog: type, + }); + + const logs = (result.log as string[]).slice(0, limit); + + return { + content: [ + { + text: `Found: ${result.totalLinesWritten} messages`, + type: "text", + }, + + ...logs.map( + (log) => + ({ + text: log, + type: "text", + }) as const + ), + ], + }; + } +} diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index d818c7ab..2ef1aee0 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -1,8 +1,9 @@ import { z } from "zod"; -import { ToolArgs, ToolBase, ToolCategory } from "../tool.js"; +import { ToolArgs, ToolBase, ToolCategory, TelemetryToolMetadata } from "../tool.js"; import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { ErrorCodes, MongoDBError } from "../../errors.js"; +import logger, { LogId } from "../../logger.js"; export const DbOperationArgs = { database: z.string().describe("Database name"), @@ -14,7 +15,16 @@ export abstract class MongoDBToolBase extends ToolBase { protected async ensureConnected(): Promise { if (!this.session.serviceProvider && this.config.connectionString) { - await this.connectToMongoDB(this.config.connectionString); + try { + await this.connectToMongoDB(this.config.connectionString); + } catch (error) { + logger.error( + LogId.mongodbConnectFailure, + "mongodbTool", + `Failed to connect to MongoDB instance using the connection string from the config: ${error as string}` + ); + throw new MongoDBError(ErrorCodes.MisconfiguredConnectionString, "Not connected to MongoDB."); + } } if (!this.session.serviceProvider) { @@ -28,39 +38,53 @@ export abstract class MongoDBToolBase extends ToolBase { error: unknown, args: ToolArgs ): Promise | CallToolResult { - if (error instanceof MongoDBError && error.code === ErrorCodes.NotConnectedToMongoDB) { - return { - content: [ - { - type: "text", - text: "You need to connect to a MongoDB instance before you can access its data.", - }, - { - type: "text", - text: "Please use the 'connect' tool to connect to a MongoDB instance.", - }, - ], - isError: true, - }; + if (error instanceof MongoDBError) { + 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.", + }, + { + type: "text", + text: "Please use the 'connect' or 'switch-connection' tool to connect to a MongoDB instance.", + }, + ], + 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. Alternatively, use the 'switch-connection' tool to connect to a different instance.", + }, + ], + isError: true, + }; + } } return super.handleError(error, args); } - protected async connectToMongoDB(connectionString: string): Promise { - const provider = await NodeDriverServiceProvider.connect(connectionString, { - productDocsLink: "https://docs.mongodb.com/todo-mcp", - productName: "MongoDB MCP", - readConcern: { - level: this.config.connectOptions.readConcern, - }, - readPreference: this.config.connectOptions.readPreference, - writeConcern: { - w: this.config.connectOptions.writeConcern, - }, - timeoutMS: this.config.connectOptions.timeoutMS, - }); + protected connectToMongoDB(connectionString: string): Promise { + return this.session.connectToMongoDB(connectionString, this.config.connectOptions); + } + + protected resolveTelemetryMetadata( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + args: ToolArgs + ): TelemetryToolMetadata { + const metadata: TelemetryToolMetadata = {}; + + // Add projectId to the metadata if running a MongoDB operation to an Atlas cluster + if (this.session.connectedAtlasCluster?.projectId) { + metadata.projectId = this.session.connectedAtlasCluster.projectId; + } - this.session.serviceProvider = provider; + return metadata; } } diff --git a/src/tools/mongodb/read/aggregate.ts b/src/tools/mongodb/read/aggregate.ts index c5824785..c1a46c71 100644 --- a/src/tools/mongodb/read/aggregate.ts +++ b/src/tools/mongodb/read/aggregate.ts @@ -5,7 +5,7 @@ import { ToolArgs, OperationType } from "../../tool.js"; import { EJSON } from "bson"; export const AggregateArgs = { - pipeline: z.array(z.object({}).passthrough()).describe("An array of aggregation stages to execute"), + pipeline: z.array(z.record(z.string(), z.unknown())).describe("An array of aggregation stages to execute"), }; export class AggregateTool extends MongoDBToolBase { diff --git a/src/tools/mongodb/read/count.ts b/src/tools/mongodb/read/count.ts index 188648d5..5d97afa9 100644 --- a/src/tools/mongodb/read/count.ts +++ b/src/tools/mongodb/read/count.ts @@ -5,17 +5,17 @@ import { z } from "zod"; export const CountArgs = { query: z - .object({}) - .passthrough() + .record(z.string(), z.unknown()) .optional() .describe( - "The query filter to count documents. Matches the syntax of the filter argument of db.collection.count()" + "A filter/query parameter. Allows users to filter the documents to count. Matches the syntax of the filter argument of db.collection.count()." ), }; export class CountTool extends MongoDBToolBase { protected name = "count"; - protected description = "Gets the number of documents in a MongoDB collection"; + protected description = + "Gets the number of documents in a MongoDB collection using db.collection.count() and query as an optional filter parameter"; protected argsShape = { ...DbOperationArgs, ...CountArgs, diff --git a/src/tools/mongodb/read/find.ts b/src/tools/mongodb/read/find.ts index e0f806b0..c117cf58 100644 --- a/src/tools/mongodb/read/find.ts +++ b/src/tools/mongodb/read/find.ts @@ -7,13 +7,11 @@ import { EJSON } from "bson"; export const FindArgs = { filter: z - .object({}) - .passthrough() + .record(z.string(), z.unknown()) .optional() .describe("The query filter, matching the syntax of the query argument of db.collection.find()"), projection: z - .object({}) - .passthrough() + .record(z.string(), z.unknown()) .optional() .describe("The projection, matching the syntax of the projection argument of db.collection.find()"), limit: z.number().optional().default(10).describe("The maximum number of documents to return"), diff --git a/src/tools/mongodb/tools.ts b/src/tools/mongodb/tools.ts index eddbd26b..d64d53ea 100644 --- a/src/tools/mongodb/tools.ts +++ b/src/tools/mongodb/tools.ts @@ -17,6 +17,7 @@ import { DropDatabaseTool } from "./delete/dropDatabase.js"; import { DropCollectionTool } from "./delete/dropCollection.js"; import { ExplainTool } from "./metadata/explain.js"; import { CreateCollectionTool } from "./create/createCollection.js"; +import { LogsTool } from "./metadata/logs.js"; export const MongoDbTools = [ ConnectTool, @@ -38,4 +39,5 @@ export const MongoDbTools = [ DropCollectionTool, ExplainTool, CreateCollectionTool, + LogsTool, ]; diff --git a/src/tools/mongodb/update/updateMany.ts b/src/tools/mongodb/update/updateMany.ts index c11d8a49..187e4633 100644 --- a/src/tools/mongodb/update/updateMany.ts +++ b/src/tools/mongodb/update/updateMany.ts @@ -9,15 +9,13 @@ export class UpdateManyTool extends MongoDBToolBase { protected argsShape = { ...DbOperationArgs, filter: z - .object({}) - .passthrough() + .record(z.string(), z.unknown()) .optional() .describe( "The selection criteria for the update, matching the syntax of the filter argument of db.collection.updateOne()" ), update: z - .object({}) - .passthrough() + .record(z.string(), z.unknown()) .describe("An update document describing the modifications to apply using update operator expressions"), upsert: z .boolean() diff --git a/src/tools/tool.ts b/src/tools/tool.ts index a37c7224..37375f70 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -1,17 +1,20 @@ -import { z, type ZodRawShape, type ZodNever } from "zod"; -import type { McpServer, ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { z, type ZodRawShape, type ZodNever, AnyZodObject } from "zod"; +import type { McpServer, RegisteredTool, ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { CallToolResult, ToolAnnotations } from "@modelcontextprotocol/sdk/types.js"; import { Session } from "../session.js"; -import logger from "../logger.js"; -import { mongoLogId } from "mongodb-log-writer"; +import logger, { LogId } from "../logger.js"; import { Telemetry } from "../telemetry/telemetry.js"; import { type ToolEvent } from "../telemetry/types.js"; import { UserConfig } from "../config.js"; export type ToolArgs = z.objectOutputType; -export type OperationType = "metadata" | "read" | "create" | "delete" | "update" | "cluster"; +export type OperationType = "metadata" | "read" | "create" | "delete" | "update"; export type ToolCategory = "mongodb" | "atlas"; +export type TelemetryToolMetadata = { + projectId?: string; + orgId?: string; +}; export abstract class ToolBase { protected abstract name: string; @@ -24,6 +27,34 @@ export abstract class ToolBase { protected abstract argsShape: ZodRawShape; + protected get annotations(): ToolAnnotations { + const annotations: ToolAnnotations = { + title: this.name, + description: this.description, + }; + + switch (this.operationType) { + case "read": + case "metadata": + annotations.readOnlyHint = true; + annotations.destructiveHint = false; + break; + case "delete": + annotations.readOnlyHint = false; + annotations.destructiveHint = true; + break; + case "create": + case "update": + annotations.destructiveHint = false; + annotations.readOnlyHint = false; + break; + default: + break; + } + + return annotations; + } + protected abstract execute(...args: Parameters>): Promise; constructor( @@ -32,29 +63,6 @@ export abstract class ToolBase { protected readonly telemetry: Telemetry ) {} - /** - * Creates and emits a tool telemetry event - * @param startTime - Start time in milliseconds - * @param result - Whether the command succeeded or failed - * @param error - Optional error if the command failed - */ - private async emitToolEvent(startTime: number, result: CallToolResult): Promise { - const duration = Date.now() - startTime; - const event: ToolEvent = { - timestamp: new Date().toISOString(), - source: "mdbmcp", - properties: { - ...this.telemetry.getCommonProperties(), - command: this.name, - category: this.category, - component: "tool", - duration_ms: duration, - result: result.isError ? "failure" : "success", - }, - }; - await this.telemetry.emitEvents([event]); - } - public register(server: McpServer): void { if (!this.verifyAllowed()) { return; @@ -63,30 +71,67 @@ export abstract class ToolBase { const callback: ToolCallback = async (...args) => { const startTime = Date.now(); try { - logger.debug( - mongoLogId(1_000_006), - "tool", - `Executing ${this.name} with args: ${JSON.stringify(args)}` - ); + logger.debug(LogId.toolExecute, "tool", `Executing tool ${this.name}`); const result = await this.execute(...args); - await this.emitToolEvent(startTime, result); + this.emitToolEvent(startTime, result, ...args); return result; } catch (error: unknown) { - logger.error(mongoLogId(1_000_000), "tool", `Error executing ${this.name}: ${error as string}`); + logger.error(LogId.toolExecuteFailure, "tool", `Error executing ${this.name}: ${error as string}`); const toolResult = await this.handleError(error, args[0] as ToolArgs); - await this.emitToolEvent(startTime, toolResult).catch(() => {}); + this.emitToolEvent(startTime, toolResult, ...args); return toolResult; } }; - server.tool(this.name, this.description, this.argsShape, callback); + server.tool(this.name, this.description, this.argsShape, this.annotations, callback); + + // This is very similar to RegisteredTool.update, but without the bugs around the name. + // In the upstream update method, the name is captured in the closure and not updated when + // the tool name changes. This means that you only get one name update before things end up + // in a broken state. + this.update = (updates: { name?: string; description?: string; inputSchema?: AnyZodObject }) => { + const tools = server["_registeredTools"] as { [toolName: string]: RegisteredTool }; + const existingTool = tools[this.name]; + + if (!existingTool) { + logger.warning(LogId.toolUpdateFailure, "tool", `Tool ${this.name} not found in update`); + return; + } + + existingTool.annotations = this.annotations; + + if (updates.name && updates.name !== this.name) { + existingTool.annotations.title = updates.name; + delete tools[this.name]; + this.name = updates.name; + tools[this.name] = existingTool; + } + + if (updates.description) { + existingTool.annotations.description = updates.description; + existingTool.description = updates.description; + this.description = updates.description; + } + + if (updates.inputSchema) { + existingTool.inputSchema = updates.inputSchema; + } + + server.sendToolListChanged(); + }; } + protected update?: (updates: { name?: string; description?: string; inputSchema?: AnyZodObject }) => void; + // Checks if a tool is allowed to run based on the config protected verifyAllowed(): boolean { let errorClarification: string | undefined; - if (this.config.disabledTools.includes(this.category)) { + + // Check read-only mode first + if (this.config.readOnly && !["read", "metadata"].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}\`,`; } else if (this.config.disabledTools.includes(this.operationType)) { errorClarification = `its operation type, \`${this.operationType}\`,`; @@ -96,7 +141,7 @@ export abstract class ToolBase { if (errorClarification) { logger.debug( - mongoLogId(1_000_010), + LogId.toolDisabled, "tool", `Prevented registration of ${this.name} because ${errorClarification} is disabled in the config` ); @@ -118,9 +163,52 @@ export abstract class ToolBase { { type: "text", text: `Error running ${this.name}: ${error instanceof Error ? error.message : String(error)}`, - isError: true, }, ], + isError: true, }; } + + protected abstract resolveTelemetryMetadata( + ...args: Parameters> + ): TelemetryToolMetadata; + + /** + * Creates and emits a tool telemetry event + * @param startTime - Start time in milliseconds + * @param result - Whether the command succeeded or failed + * @param args - The arguments passed to the tool + */ + private emitToolEvent( + startTime: number, + result: CallToolResult, + ...args: Parameters> + ): void { + if (!this.telemetry.isTelemetryEnabled()) { + return; + } + const duration = Date.now() - startTime; + const metadata = this.resolveTelemetryMetadata(...args); + const event: ToolEvent = { + timestamp: new Date().toISOString(), + source: "mdbmcp", + properties: { + command: this.name, + category: this.category, + component: "tool", + duration_ms: duration, + result: result.isError ? "failure" : "success", + }, + }; + + if (metadata?.orgId) { + event.properties.org_id = metadata.orgId; + } + + if (metadata?.projectId) { + event.properties.project_id = metadata.projectId; + } + + this.telemetry.emitEvents([event]); + } } diff --git a/src/types/mongodb-connection-string-url.d.ts b/src/types/mongodb-connection-string-url.d.ts new file mode 100644 index 00000000..01a0cff2 --- /dev/null +++ b/src/types/mongodb-connection-string-url.d.ts @@ -0,0 +1,69 @@ +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/native-machine-id.d.ts b/src/types/native-machine-id.d.ts deleted file mode 100644 index 153dbf38..00000000 --- a/src/types/native-machine-id.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Type definitions for native-machine-id - * Provides functionality to retrieve the machine ID of the current device. - */ - -declare module "native-machine-id" { - /** - * Gets the machine ID synchronously. - * @returns A string containing the machine ID. - */ - export function getMachineIdSync(): string; - - /** - * Gets the machine ID asynchronously. - * @returns A Promise that resolves to a string containing the machine ID. - */ - export function getMachineId(): Promise; - - /** - * Gets a machine ID that is based on the original ID but is "hashed" for privacy. - * @param {string} [original] - The original ID to hash. If not provided, gets the machine ID first. - * @param {string} [type='md5'] - The hashing algorithm to use. - * @returns A Promise that resolves to a string containing the hashed machine ID. - */ - export function machineIdSync(original?: string, type?: string): string; - - /** - * Gets a machine ID that is based on the original ID but is "hashed" for privacy. - * @param {string} [original] - The original ID to hash. If not provided, gets the machine ID first. - * @param {string} [type='md5'] - The hashing algorithm to use. - * @returns A Promise that resolves to a string containing the hashed machine ID. - */ - export function machineId(original?: string, type?: string): Promise; -} diff --git a/tests/integration/helpers.ts b/tests/integration/helpers.ts index 5b7ebe1c..e407e250 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -1,12 +1,13 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { InMemoryTransport } from "./inMemoryTransport.js"; import { Server } from "../../src/server.js"; -import { ObjectId } from "mongodb"; -import { config, UserConfig } from "../../src/config.js"; +import { UserConfig } from "../../src/config.js"; import { McpError } from "@modelcontextprotocol/sdk/types.js"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { Session } from "../../src/session.js"; -import { toIncludeAllMembers } from "jest-extended"; +import { Telemetry } from "../../src/telemetry/telemetry.js"; +import { config } from "../../src/config.js"; +import { jest } from "@jest/globals"; interface ParameterInfo { name: string; @@ -21,22 +22,25 @@ export interface IntegrationTest { mcpClient: () => Client; mcpServer: () => Server; } +export const defaultTestConfig: UserConfig = { + ...config, + telemetry: "disabled", +}; -export function setupIntegrationTest(userConfig: UserConfig = config): IntegrationTest { +export function setupIntegrationTest(getUserConfig: () => UserConfig): IntegrationTest { let mcpClient: Client | undefined; let mcpServer: Server | undefined; - let randomDbName: string; - beforeAll(async () => { + const userConfig = getUserConfig(); const clientTransport = new InMemoryTransport(); const serverTransport = new InMemoryTransport(); await serverTransport.start(); await clientTransport.start(); - clientTransport.output.pipeTo(serverTransport.input); - serverTransport.output.pipeTo(clientTransport.input); + void clientTransport.output.pipeTo(serverTransport.input); + void serverTransport.output.pipeTo(clientTransport.input); mcpClient = new Client( { @@ -54,21 +58,35 @@ export function setupIntegrationTest(userConfig: UserConfig = config): Integrati apiClientSecret: userConfig.apiClientSecret, }); + // Mock hasValidAccessToken for tests + if (userConfig.apiClientId && userConfig.apiClientSecret) { + const mockFn = jest.fn<() => Promise>().mockResolvedValue(true); + // @ts-expect-error accessing private property for testing + session.apiClient.validateAccessToken = mockFn; + } + + userConfig.telemetry = "disabled"; + + const telemetry = Telemetry.create(session, userConfig); + mcpServer = new Server({ session, userConfig, + telemetry, mcpServer: new McpServer({ name: "test-server", - version: "1.2.3", + version: "5.2.3", }), }); + await mcpServer.connect(serverTransport); await mcpClient.connect(clientTransport); }); - beforeEach(async () => { - config.telemetry = "disabled"; - randomDbName = new ObjectId().toString(); + afterEach(async () => { + if (mcpServer) { + await mcpServer.session.close(); + } }); afterAll(async () => { @@ -101,14 +119,20 @@ export function setupIntegrationTest(userConfig: UserConfig = config): Integrati }; } -export function getResponseContent(content: unknown): string { +// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents +export function getResponseContent(content: unknown | { content: unknown }): string { return getResponseElements(content) .map((item) => item.text) .join("\n"); } -export function getResponseElements(content: unknown): { type: string; text: string }[] { - expect(Array.isArray(content)).toBe(true); +// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents +export function getResponseElements(content: unknown | { content: unknown }): { type: string; text: string }[] { + if (typeof content === "object" && content !== null && "content" in content) { + content = (content as { content: unknown }).content; + } + + expect(content).toBeArray(); const response = content as { type: string; text: string }[]; for (const item of response) { @@ -129,9 +153,9 @@ export async function connect(client: Client, connectionString: string): Promise export function getParameters(tool: ToolInfo): ParameterInfo[] { expect(tool.inputSchema.type).toBe("object"); - expect(tool.inputSchema.properties).toBeDefined(); + expectDefined(tool.inputSchema.properties); - return Object.entries(tool.inputSchema.properties!) + return Object.entries(tool.inputSchema.properties) .sort((a, b) => a[0].localeCompare(b[0])) .map(([key, value]) => { expect(value).toHaveProperty("type"); @@ -163,13 +187,12 @@ export const databaseCollectionInvalidArgs = [ { database: "test" }, { collection: "foo" }, { database: 123, collection: "foo" }, - { database: "test", collection: "foo", extra: "bar" }, { database: "test", collection: 123 }, { database: [], collection: "foo" }, { database: "test", collection: [] }, ]; -export const databaseInvalidArgs = [{}, { database: 123 }, { database: [] }, { database: "test", extra: "bar" }]; +export const databaseInvalidArgs = [{}, { database: 123 }, { database: [] }]; export function validateToolMetadata( integration: IntegrationTest, @@ -179,10 +202,11 @@ export function validateToolMetadata( ): void { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const tool = tools.find((tool) => tool.name === name)!; - expect(tool).toBeDefined(); + const tool = tools.find((tool) => tool.name === name); + expectDefined(tool); expect(tool.description).toBe(description); + validateToolAnnotations(tool, name, description); const toolParameters = getParameters(tool); expect(toolParameters).toHaveLength(parameters.length); expect(toolParameters).toIncludeAllMembers(parameters); @@ -199,8 +223,9 @@ export function validateThrowsForInvalidArguments( it(`throws a schema error for: ${JSON.stringify(arg)}`, async () => { try { await integration.mcpClient().callTool({ name, arguments: arg }); - expect.fail("Expected an error to be thrown"); + throw new Error("Expected an error to be thrown"); } catch (error) { + expect((error as Error).message).not.toEqual("Expected an error to be thrown"); expect(error).toBeInstanceOf(McpError); const mcpError = error as McpError; expect(mcpError.code).toEqual(-32602); @@ -210,3 +235,31 @@ export function validateThrowsForInvalidArguments( } }); } + +/** Expects the argument being defined and asserts it */ +export function expectDefined(arg: T): asserts arg is Exclude { + expect(arg).toBeDefined(); + expect(arg).not.toBeNull(); +} + +function validateToolAnnotations(tool: ToolInfo, name: string, description: string): void { + expectDefined(tool.annotations); + expect(tool.annotations.title).toBe(name); + expect(tool.annotations.description).toBe(description); + + switch (tool.operationType) { + case "read": + case "metadata": + expect(tool.annotations.readOnlyHint).toBe(true); + expect(tool.annotations.destructiveHint).toBe(false); + break; + case "delete": + expect(tool.annotations.readOnlyHint).toBe(false); + expect(tool.annotations.destructiveHint).toBe(true); + break; + case "create": + case "update": + expect(tool.annotations.readOnlyHint).toBe(false); + expect(tool.annotations.destructiveHint).toBe(false); + } +} diff --git a/tests/integration/inMemoryTransport.ts b/tests/integration/inMemoryTransport.ts index a12c4625..daaf577a 100644 --- a/tests/integration/inMemoryTransport.ts +++ b/tests/integration/inMemoryTransport.ts @@ -2,7 +2,7 @@ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; export class InMemoryTransport implements Transport { - private outputController: ReadableStreamDefaultController; + private outputController: ReadableStreamDefaultController | undefined; private startPromise: Promise; @@ -35,12 +35,13 @@ export class InMemoryTransport implements Transport { } send(message: JSONRPCMessage): Promise { - this.outputController.enqueue(message); + this.outputController?.enqueue(message); return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/require-await async close(): Promise { - this.outputController.close(); + this.outputController?.close(); this.onclose?.(); } onclose?: (() => void) | undefined; @@ -49,10 +50,10 @@ export class InMemoryTransport implements Transport { sessionId?: string | undefined; private static getPromise(): [Promise, resolve: () => void] { - let resolve: () => void; + let resolve: () => void = () => {}; const promise = new Promise((res) => { resolve = res; }); - return [promise, resolve!]; + return [promise, resolve]; } } diff --git a/tests/integration/server.test.ts b/tests/integration/server.test.ts index 5130b4b6..3b4c1858 100644 --- a/tests/integration/server.test.ts +++ b/tests/integration/server.test.ts @@ -1,61 +1,86 @@ -import { setupIntegrationTest } from "./helpers"; -import { config } from "../../src/config.js"; +import { defaultTestConfig, expectDefined, setupIntegrationTest } from "./helpers.js"; +import { describeWithMongoDB } from "./tools/mongodb/mongodbHelpers.js"; describe("Server integration test", () => { - describe("without atlas", () => { - const integration = setupIntegrationTest({ - ...config, + describeWithMongoDB( + "without atlas", + (integration) => { + it("should return positive number of tools and have no atlas tools", async () => { + const tools = await integration.mcpClient().listTools(); + expectDefined(tools); + expect(tools.tools.length).toBeGreaterThan(0); + + const atlasTools = tools.tools.filter((tool) => tool.name.startsWith("atlas-")); + expect(atlasTools.length).toBeLessThanOrEqual(0); + }); + }, + () => ({ + ...defaultTestConfig, apiClientId: undefined, apiClientSecret: undefined, - }); + }) + ); - it("should return positive number of tools and have no atlas tools", async () => { - const tools = await integration.mcpClient().listTools(); - expect(tools).toBeDefined(); - expect(tools.tools.length).toBeGreaterThan(0); - - const atlasTools = tools.tools.filter((tool) => tool.name.startsWith("atlas-")); - expect(atlasTools.length).toBeLessThanOrEqual(0); - }); - }); describe("with atlas", () => { - const integration = setupIntegrationTest({ - ...config, + const integration = setupIntegrationTest(() => ({ + ...defaultTestConfig, apiClientId: "test", apiClientSecret: "test", - }); + })); describe("list capabilities", () => { it("should return positive number of tools and have some atlas tools", async () => { const tools = await integration.mcpClient().listTools(); - expect(tools).toBeDefined(); + expectDefined(tools); expect(tools.tools.length).toBeGreaterThan(0); const atlasTools = tools.tools.filter((tool) => tool.name.startsWith("atlas-")); expect(atlasTools.length).toBeGreaterThan(0); }); - it("should return no resources", async () => { - await expect(() => integration.mcpClient().listResources()).rejects.toMatchObject({ - message: "MCP error -32601: Method not found", - }); - }); - it("should return no prompts", async () => { await expect(() => integration.mcpClient().listPrompts()).rejects.toMatchObject({ message: "MCP error -32601: Method not found", }); }); - it("should return capabilities", async () => { + it("should return capabilities", () => { const capabilities = integration.mcpClient().getServerCapabilities(); - expect(capabilities).toBeDefined(); - expect(capabilities?.completions).toBeUndefined(); - expect(capabilities?.experimental).toBeUndefined(); - expect(capabilities?.tools).toBeDefined(); - expect(capabilities?.logging).toBeDefined(); + expectDefined(capabilities); + expect(capabilities.completions).toBeUndefined(); + expect(capabilities.experimental).toBeUndefined(); + expectDefined(capabilities?.tools); + expectDefined(capabilities?.logging); expect(capabilities?.prompts).toBeUndefined(); }); }); }); + + describe("with read-only mode", () => { + const integration = setupIntegrationTest(() => ({ + ...defaultTestConfig, + readOnly: true, + apiClientId: "test", + apiClientSecret: "test", + })); + + it("should only register read and metadata operation tools when read-only mode is enabled", async () => { + const tools = await integration.mcpClient().listTools(); + expectDefined(tools); + expect(tools.tools.length).toBeGreaterThan(0); + + // Check that we have some tools available (the read and metadata ones) + expect(tools.tools.some((tool) => tool.name === "find")).toBe(true); + expect(tools.tools.some((tool) => tool.name === "collection-schema")).toBe(true); + expect(tools.tools.some((tool) => tool.name === "list-databases")).toBe(true); + expect(tools.tools.some((tool) => tool.name === "atlas-list-orgs")).toBe(true); + expect(tools.tools.some((tool) => tool.name === "atlas-list-projects")).toBe(true); + + // Check that non-read tools are NOT available + expect(tools.tools.some((tool) => tool.name === "insert-one")).toBe(false); + expect(tools.tools.some((tool) => tool.name === "update-many")).toBe(false); + expect(tools.tools.some((tool) => tool.name === "delete-one")).toBe(false); + expect(tools.tools.some((tool) => tool.name === "drop-collection")).toBe(false); + }); + }); }); diff --git a/tests/integration/tools/atlas/accessLists.test.ts b/tests/integration/tools/atlas/accessLists.test.ts index 43e5742d..d2dc219c 100644 --- a/tests/integration/tools/atlas/accessLists.test.ts +++ b/tests/integration/tools/atlas/accessLists.test.ts @@ -1,5 +1,6 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { describeWithAtlas, withProject } from "./atlasHelpers.js"; +import { expectDefined } from "../../helpers.js"; function generateRandomIp() { const randomIp: number[] = [192]; @@ -41,10 +42,10 @@ describeWithAtlas("ip access lists", (integration) => { describe("atlas-create-access-list", () => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const createAccessList = tools.find((tool) => tool.name === "atlas-create-access-list")!; - expect(createAccessList).toBeDefined(); + const createAccessList = tools.find((tool) => tool.name === "atlas-create-access-list"); + expectDefined(createAccessList); expect(createAccessList.inputSchema.type).toBe("object"); - expect(createAccessList.inputSchema.properties).toBeDefined(); + expectDefined(createAccessList.inputSchema.properties); expect(createAccessList.inputSchema.properties).toHaveProperty("projectId"); expect(createAccessList.inputSchema.properties).toHaveProperty("ipAddresses"); expect(createAccessList.inputSchema.properties).toHaveProperty("cidrBlocks"); @@ -66,17 +67,17 @@ describeWithAtlas("ip access lists", (integration) => { })) as CallToolResult; expect(response.content).toBeArray(); expect(response.content).toHaveLength(1); - expect(response.content[0].text).toContain("IP/CIDR ranges added to access list"); + expect(response.content[0]?.text).toContain("IP/CIDR ranges added to access list"); }); }); describe("atlas-inspect-access-list", () => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const inspectAccessList = tools.find((tool) => tool.name === "atlas-inspect-access-list")!; - expect(inspectAccessList).toBeDefined(); + const inspectAccessList = tools.find((tool) => tool.name === "atlas-inspect-access-list"); + expectDefined(inspectAccessList); expect(inspectAccessList.inputSchema.type).toBe("object"); - expect(inspectAccessList.inputSchema.properties).toBeDefined(); + expectDefined(inspectAccessList.inputSchema.properties); expect(inspectAccessList.inputSchema.properties).toHaveProperty("projectId"); }); @@ -89,7 +90,7 @@ describeWithAtlas("ip access lists", (integration) => { expect(response.content).toBeArray(); expect(response.content).toHaveLength(1); for (const value of values) { - expect(response.content[0].text).toContain(value); + expect(response.content[0]?.text).toContain(value); } }); }); diff --git a/tests/integration/tools/atlas/alerts.test.ts b/tests/integration/tools/atlas/alerts.test.ts new file mode 100644 index 00000000..3e95cced --- /dev/null +++ b/tests/integration/tools/atlas/alerts.test.ts @@ -0,0 +1,42 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { expectDefined } from "../../helpers.js"; +import { parseTable, describeWithAtlas, withProject } from "./atlasHelpers.js"; + +describeWithAtlas("alerts", (integration) => { + describe("atlas-list-alerts", () => { + it("should have correct metadata", async () => { + const { tools } = await integration.mcpClient().listTools(); + const listAlerts = tools.find((tool) => tool.name === "atlas-list-alerts"); + expectDefined(listAlerts); + expect(listAlerts.inputSchema.type).toBe("object"); + expectDefined(listAlerts.inputSchema.properties); + expect(listAlerts.inputSchema.properties).toHaveProperty("projectId"); + }); + + withProject(integration, ({ getProjectId }) => { + it("returns alerts in table format", async () => { + const response = (await integration.mcpClient().callTool({ + name: "atlas-list-alerts", + arguments: { projectId: getProjectId() }, + })) as CallToolResult; + + expect(response.content).toBeArray(); + expect(response.content).toHaveLength(1); + + const data = parseTable(response.content[0]?.text as string); + expect(data).toBeArray(); + + // Since we can't guarantee alerts will exist, we just verify the table structure + if (data.length > 0) { + const alert = data[0]; + expect(alert).toHaveProperty("Alert ID"); + expect(alert).toHaveProperty("Status"); + expect(alert).toHaveProperty("Created"); + expect(alert).toHaveProperty("Updated"); + expect(alert).toHaveProperty("Type"); + expect(alert).toHaveProperty("Comment"); + } + }); + }); + }); +}); diff --git a/tests/integration/tools/atlas/atlasHelpers.ts b/tests/integration/tools/atlas/atlasHelpers.ts index 36b88c1e..f03e1dc7 100644 --- a/tests/integration/tools/atlas/atlasHelpers.ts +++ b/tests/integration/tools/atlas/atlasHelpers.ts @@ -1,17 +1,18 @@ import { ObjectId } from "mongodb"; import { Group } from "../../../../src/common/atlas/openapi.js"; import { ApiClient } from "../../../../src/common/atlas/apiClient.js"; -import { setupIntegrationTest, IntegrationTest } from "../../helpers.js"; +import { setupIntegrationTest, IntegrationTest, defaultTestConfig } from "../../helpers.js"; export type IntegrationTestFunction = (integration: IntegrationTest) => void; -export function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -export function describeWithAtlas(name: number | string | Function | jest.FunctionLike, fn: IntegrationTestFunction) { +export function describeWithAtlas(name: string, fn: IntegrationTestFunction) { const testDefinition = () => { - const integration = setupIntegrationTest(); + const integration = setupIntegrationTest(() => ({ + ...defaultTestConfig, + apiClientId: process.env.MDB_MCP_API_CLIENT_ID, + apiClientSecret: process.env.MDB_MCP_API_CLIENT_SECRET, + })); + describe(name, () => { fn(integration); }); @@ -72,9 +73,11 @@ export function parseTable(text: string): Record[] { return data .filter((_, index) => index >= 2) .map((cells) => { - const row = {}; + const row: Record = {}; cells.forEach((cell, index) => { - row[headers[index]] = cell; + if (headers) { + row[headers[index] ?? ""] = cell; + } }); return row; }); @@ -86,14 +89,14 @@ async function createProject(apiClient: ApiClient): Promise { const projectName: string = `testProj-` + randomId; const orgs = await apiClient.listOrganizations(); - if (!orgs?.results?.length || !orgs.results[0].id) { + if (!orgs?.results?.length || !orgs.results[0]?.id) { throw new Error("No orgs found"); } const group = await apiClient.createProject({ body: { name: projectName, - orgId: orgs.results[0].id, + orgId: orgs.results[0]?.id ?? "", } as Group, }); diff --git a/tests/integration/tools/atlas/clusters.test.ts b/tests/integration/tools/atlas/clusters.test.ts index 72f41df0..166ee637 100644 --- a/tests/integration/tools/atlas/clusters.test.ts +++ b/tests/integration/tools/atlas/clusters.test.ts @@ -1,13 +1,18 @@ import { Session } from "../../../../src/session.js"; -import { describeWithAtlas, withProject, sleep, randomId } from "./atlasHelpers.js"; +import { expectDefined } from "../../helpers.js"; +import { describeWithAtlas, withProject, randomId } from "./atlasHelpers.js"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + async function deleteAndWaitCluster(session: Session, projectId: string, clusterName: string) { await session.apiClient.deleteCluster({ params: { path: { groupId: projectId, - clusterName: clusterName, + clusterName, }, }, }); @@ -17,7 +22,7 @@ async function deleteAndWaitCluster(session: Session, projectId: string, cluster params: { path: { groupId: projectId, - clusterName: clusterName, + clusterName, }, }, }); @@ -28,6 +33,23 @@ async function deleteAndWaitCluster(session: Session, projectId: string, cluster } } +async function waitClusterState(session: Session, projectId: string, clusterName: string, state: string) { + while (true) { + const cluster = await session.apiClient.getCluster({ + params: { + path: { + groupId: projectId, + clusterName, + }, + }, + }); + if (cluster?.stateName === state) { + return; + } + await sleep(1000); + } +} + describeWithAtlas("clusters", (integration) => { withProject(integration, ({ getProjectId }) => { const clusterName = "ClusterTest-" + randomId; @@ -43,11 +65,11 @@ describeWithAtlas("clusters", (integration) => { describe("atlas-create-free-cluster", () => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const createFreeCluster = tools.find((tool) => tool.name === "atlas-create-free-cluster")!; + const createFreeCluster = tools.find((tool) => tool.name === "atlas-create-free-cluster"); - expect(createFreeCluster).toBeDefined(); + expectDefined(createFreeCluster); expect(createFreeCluster.inputSchema.type).toBe("object"); - expect(createFreeCluster.inputSchema.properties).toBeDefined(); + expectDefined(createFreeCluster.inputSchema.properties); expect(createFreeCluster.inputSchema.properties).toHaveProperty("projectId"); expect(createFreeCluster.inputSchema.properties).toHaveProperty("name"); expect(createFreeCluster.inputSchema.properties).toHaveProperty("region"); @@ -65,19 +87,19 @@ describeWithAtlas("clusters", (integration) => { }, })) as CallToolResult; expect(response.content).toBeArray(); - expect(response.content).toHaveLength(1); - expect(response.content[0].text).toContain("has been created"); + expect(response.content).toHaveLength(2); + expect(response.content[0]?.text).toContain("has been created"); }); }); describe("atlas-inspect-cluster", () => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const inspectCluster = tools.find((tool) => tool.name === "atlas-inspect-cluster")!; + const inspectCluster = tools.find((tool) => tool.name === "atlas-inspect-cluster"); - expect(inspectCluster).toBeDefined(); + expectDefined(inspectCluster); expect(inspectCluster.inputSchema.type).toBe("object"); - expect(inspectCluster.inputSchema.properties).toBeDefined(); + expectDefined(inspectCluster.inputSchema.properties); expect(inspectCluster.inputSchema.properties).toHaveProperty("projectId"); expect(inspectCluster.inputSchema.properties).toHaveProperty("clusterName"); }); @@ -91,17 +113,17 @@ describeWithAtlas("clusters", (integration) => { })) as CallToolResult; expect(response.content).toBeArray(); expect(response.content).toHaveLength(1); - expect(response.content[0].text).toContain(`${clusterName} | `); + expect(response.content[0]?.text).toContain(`${clusterName} | `); }); }); describe("atlas-list-clusters", () => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const listClusters = tools.find((tool) => tool.name === "atlas-list-clusters")!; - expect(listClusters).toBeDefined(); + const listClusters = tools.find((tool) => tool.name === "atlas-list-clusters"); + expectDefined(listClusters); expect(listClusters.inputSchema.type).toBe("object"); - expect(listClusters.inputSchema.properties).toBeDefined(); + expectDefined(listClusters.inputSchema.properties); expect(listClusters.inputSchema.properties).toHaveProperty("projectId"); }); @@ -113,7 +135,50 @@ describeWithAtlas("clusters", (integration) => { .callTool({ name: "atlas-list-clusters", arguments: { projectId } })) as CallToolResult; expect(response.content).toBeArray(); expect(response.content).toHaveLength(2); - expect(response.content[1].text).toContain(`${clusterName} | `); + expect(response.content[1]?.text).toContain(`${clusterName} | `); + }); + }); + + describe("atlas-connect-cluster", () => { + beforeAll(async () => { + const projectId = getProjectId(); + await waitClusterState(integration.mcpServer().session, projectId, clusterName, "IDLE"); + await integration.mcpServer().session.apiClient.createProjectIpAccessList({ + params: { + path: { + groupId: projectId, + }, + }, + body: [ + { + comment: "MCP test", + cidrBlock: "0.0.0.0/0", + }, + ], + }); + }); + + it("should have correct metadata", async () => { + const { tools } = await integration.mcpClient().listTools(); + const connectCluster = tools.find((tool) => tool.name === "atlas-connect-cluster"); + + expectDefined(connectCluster); + expect(connectCluster.inputSchema.type).toBe("object"); + expectDefined(connectCluster.inputSchema.properties); + expect(connectCluster.inputSchema.properties).toHaveProperty("projectId"); + expect(connectCluster.inputSchema.properties).toHaveProperty("clusterName"); + }); + + it("connects to cluster", async () => { + const projectId = getProjectId(); + + const response = (await integration.mcpClient().callTool({ + name: "atlas-connect-cluster", + arguments: { projectId, clusterName }, + })) as CallToolResult; + expect(response.content).toBeArray(); + expect(response.content).toHaveLength(1); + expect(response.content[0]?.text).toContain(`Connected to cluster "${clusterName}"`); }); }); }); diff --git a/tests/integration/tools/atlas/dbUsers.test.ts b/tests/integration/tools/atlas/dbUsers.test.ts index 2a5eb02a..3bfb979e 100644 --- a/tests/integration/tools/atlas/dbUsers.test.ts +++ b/tests/integration/tools/atlas/dbUsers.test.ts @@ -1,78 +1,104 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { Session } from "../../../../src/session.js"; import { describeWithAtlas, withProject, randomId } from "./atlasHelpers.js"; +import { expectDefined, getResponseElements } from "../../helpers.js"; +import { ApiClientError } from "../../../../src/common/atlas/apiClientError.js"; describeWithAtlas("db users", (integration) => { - const userName = "testuser-" + randomId; withProject(integration, ({ getProjectId }) => { - afterAll(async () => { - const projectId = getProjectId(); + let userName: string; + beforeEach(() => { + userName = "testuser-" + randomId; + }); - const session: Session = integration.mcpServer().session; - await session.apiClient.deleteDatabaseUser({ - params: { - path: { - groupId: projectId, - username: userName, - databaseName: "admin", - }, + const createUserWithMCP = async (password?: string): Promise => { + return await integration.mcpClient().callTool({ + name: "atlas-create-db-user", + arguments: { + projectId: getProjectId(), + username: userName, + password, + roles: [ + { + roleName: "readWrite", + databaseName: "admin", + }, + ], }, }); + }; + + afterEach(async () => { + try { + await integration.mcpServer().session.apiClient.deleteDatabaseUser({ + params: { + path: { + groupId: getProjectId(), + username: userName, + databaseName: "admin", + }, + }, + }); + } catch (error) { + // Ignore 404 errors when deleting the user + if (!(error instanceof ApiClientError) || error.response?.status !== 404) { + throw error; + } + } }); describe("atlas-create-db-user", () => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const createDbUser = tools.find((tool) => tool.name === "atlas-create-db-user")!; - expect(createDbUser).toBeDefined(); + const createDbUser = tools.find((tool) => tool.name === "atlas-create-db-user"); + expectDefined(createDbUser); expect(createDbUser.inputSchema.type).toBe("object"); - expect(createDbUser.inputSchema.properties).toBeDefined(); + expectDefined(createDbUser.inputSchema.properties); expect(createDbUser.inputSchema.properties).toHaveProperty("projectId"); expect(createDbUser.inputSchema.properties).toHaveProperty("username"); expect(createDbUser.inputSchema.properties).toHaveProperty("password"); expect(createDbUser.inputSchema.properties).toHaveProperty("roles"); expect(createDbUser.inputSchema.properties).toHaveProperty("clusters"); }); - it("should create a database user", async () => { - const projectId = getProjectId(); - const response = (await integration.mcpClient().callTool({ - name: "atlas-create-db-user", - arguments: { - projectId, - username: userName, - password: "testpassword", - roles: [ - { - roleName: "readWrite", - databaseName: "admin", - }, - ], - }, - })) as CallToolResult; - expect(response.content).toBeArray(); - expect(response.content).toHaveLength(1); - expect(response.content[0].text).toContain("created sucessfully"); + it("should create a database user with supplied password", async () => { + const response = await createUserWithMCP("testpassword"); + + const elements = getResponseElements(response); + expect(elements).toHaveLength(1); + expect(elements[0]?.text).toContain("created successfully"); + expect(elements[0]?.text).toContain(userName); + expect(elements[0]?.text).not.toContain("testpassword"); + }); + + it("should create a database user with generated password", async () => { + const response = await createUserWithMCP(); + const elements = getResponseElements(response); + expect(elements).toHaveLength(1); + expect(elements[0]?.text).toContain("created successfully"); + expect(elements[0]?.text).toContain(userName); + expect(elements[0]?.text).toContain("with password: `"); }); }); describe("atlas-list-db-users", () => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const listDbUsers = tools.find((tool) => tool.name === "atlas-list-db-users")!; - expect(listDbUsers).toBeDefined(); + const listDbUsers = tools.find((tool) => tool.name === "atlas-list-db-users"); + expectDefined(listDbUsers); expect(listDbUsers.inputSchema.type).toBe("object"); - expect(listDbUsers.inputSchema.properties).toBeDefined(); + expectDefined(listDbUsers.inputSchema.properties); expect(listDbUsers.inputSchema.properties).toHaveProperty("projectId"); }); it("returns database users by project", async () => { const projectId = getProjectId(); + await createUserWithMCP(); + const response = (await integration .mcpClient() .callTool({ name: "atlas-list-db-users", arguments: { projectId } })) as CallToolResult; expect(response.content).toBeArray(); expect(response.content).toHaveLength(1); - expect(response.content[0].text).toContain(userName); + expect(response.content[0]?.text).toContain(userName); }); }); }); diff --git a/tests/integration/tools/atlas/orgs.test.ts b/tests/integration/tools/atlas/orgs.test.ts index ca86e4b9..246a37db 100644 --- a/tests/integration/tools/atlas/orgs.test.ts +++ b/tests/integration/tools/atlas/orgs.test.ts @@ -1,5 +1,5 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { setupIntegrationTest } from "../../helpers.js"; +import { expectDefined } from "../../helpers.js"; import { parseTable, describeWithAtlas } from "./atlasHelpers.js"; describeWithAtlas("orgs", (integration) => { @@ -7,7 +7,7 @@ describeWithAtlas("orgs", (integration) => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); const listOrgs = tools.find((tool) => tool.name === "atlas-list-orgs"); - expect(listOrgs).toBeDefined(); + expectDefined(listOrgs); }); it("returns org names", async () => { @@ -16,9 +16,9 @@ describeWithAtlas("orgs", (integration) => { .callTool({ name: "atlas-list-orgs", arguments: {} })) as CallToolResult; expect(response.content).toBeArray(); expect(response.content).toHaveLength(1); - const data = parseTable(response.content[0].text as string); + const data = parseTable(response.content[0]?.text as string); expect(data).toHaveLength(1); - expect(data[0]["Organization Name"]).toEqual("MongoDB MCP Test"); + expect(data[0]?.["Organization Name"]).toEqual("MongoDB MCP Test"); }); }); }); diff --git a/tests/integration/tools/atlas/projects.test.ts b/tests/integration/tools/atlas/projects.test.ts index 3f570183..d7258fa1 100644 --- a/tests/integration/tools/atlas/projects.test.ts +++ b/tests/integration/tools/atlas/projects.test.ts @@ -1,6 +1,7 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { ObjectId } from "mongodb"; import { parseTable, describeWithAtlas } from "./atlasHelpers.js"; +import { expectDefined } from "../../helpers.js"; const randomId = new ObjectId().toString(); @@ -28,10 +29,10 @@ describeWithAtlas("projects", (integration) => { describe("atlas-create-project", () => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const createProject = tools.find((tool) => tool.name === "atlas-create-project")!; - expect(createProject).toBeDefined(); + const createProject = tools.find((tool) => tool.name === "atlas-create-project"); + expectDefined(createProject); expect(createProject.inputSchema.type).toBe("object"); - expect(createProject.inputSchema.properties).toBeDefined(); + expectDefined(createProject.inputSchema.properties); expect(createProject.inputSchema.properties).toHaveProperty("projectName"); expect(createProject.inputSchema.properties).toHaveProperty("organizationId"); }); @@ -42,16 +43,16 @@ describeWithAtlas("projects", (integration) => { })) as CallToolResult; expect(response.content).toBeArray(); expect(response.content).toHaveLength(1); - expect(response.content[0].text).toContain(projName); + expect(response.content[0]?.text).toContain(projName); }); }); describe("atlas-list-projects", () => { it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const listProjects = tools.find((tool) => tool.name === "atlas-list-projects")!; - expect(listProjects).toBeDefined(); + const listProjects = tools.find((tool) => tool.name === "atlas-list-projects"); + expectDefined(listProjects); expect(listProjects.inputSchema.type).toBe("object"); - expect(listProjects.inputSchema.properties).toBeDefined(); + expectDefined(listProjects.inputSchema.properties); expect(listProjects.inputSchema.properties).toHaveProperty("orgId"); }); @@ -61,8 +62,8 @@ describeWithAtlas("projects", (integration) => { .callTool({ name: "atlas-list-projects", arguments: {} })) as CallToolResult; expect(response.content).toBeArray(); expect(response.content).toHaveLength(1); - expect(response.content[0].text).toContain(projName); - const data = parseTable(response.content[0].text as string); + expect(response.content[0]?.text).toContain(projName); + const data = parseTable(response.content[0]?.text as string); expect(data).toBeArray(); expect(data.length).toBeGreaterThan(0); let found = false; diff --git a/tests/integration/tools/mongodb/create/createCollection.test.ts b/tests/integration/tools/mongodb/create/createCollection.test.ts index ef8da5f1..3e0d1689 100644 --- a/tests/integration/tools/mongodb/create/createCollection.test.ts +++ b/tests/integration/tools/mongodb/create/createCollection.test.ts @@ -34,7 +34,7 @@ describeWithMongoDB("createCollection tool", (integration) => { collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray(); expect(collections).toHaveLength(1); - expect(collections[0].name).toEqual("bar"); + expect(collections[0]?.name).toEqual("bar"); }); }); @@ -78,7 +78,7 @@ describeWithMongoDB("createCollection tool", (integration) => { expect(content).toEqual(`Collection "collection1" created in database "${integration.randomDbName()}".`); collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray(); expect(collections).toHaveLength(1); - expect(collections[0].name).toEqual("collection1"); + expect(collections[0]?.name).toEqual("collection1"); // Make sure we didn't drop the existing collection documents = await mongoClient.db(integration.randomDbName()).collection("collection1").find({}).toArray(); diff --git a/tests/integration/tools/mongodb/create/createIndex.test.ts b/tests/integration/tools/mongodb/create/createIndex.test.ts index fa921339..f4929b92 100644 --- a/tests/integration/tools/mongodb/create/createIndex.test.ts +++ b/tests/integration/tools/mongodb/create/createIndex.test.ts @@ -5,6 +5,7 @@ import { databaseCollectionParameters, validateToolMetadata, validateThrowsForInvalidArguments, + expectDefined, } from "../../../helpers.js"; import { IndexDirection } from "mongodb"; @@ -28,7 +29,6 @@ describeWithMongoDB("createIndex tool", (integration) => { validateThrowsForInvalidArguments(integration, "create-index", [ {}, { collection: "bar", database: 123, keys: { foo: 1 } }, - { collection: "bar", database: "test", keys: { foo: 5 } }, { collection: [], database: "test", keys: { foo: 1 } }, { collection: "bar", database: "test", keys: { foo: 1 }, name: 123 }, { collection: "bar", database: "test", keys: "foo", name: "my-index" }, @@ -38,14 +38,14 @@ describeWithMongoDB("createIndex tool", (integration) => { const mongoClient = integration.mongoClient(); const collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray(); expect(collections).toHaveLength(1); - expect(collections[0].name).toEqual("coll1"); + expect(collections[0]?.name).toEqual("coll1"); const indexes = await mongoClient.db(integration.randomDbName()).collection(collection).indexes(); expect(indexes).toHaveLength(expected.length + 1); - expect(indexes[0].name).toEqual("_id_"); + expect(indexes[0]?.name).toEqual("_id_"); for (const index of expected) { const foundIndex = indexes.find((i) => i.name === index.name); - expect(foundIndex).toBeDefined(); - expect(foundIndex!.key).toEqual(index.key); + expectDefined(foundIndex); + expect(foundIndex.key).toEqual(index.key); } }; diff --git a/tests/integration/tools/mongodb/create/insertMany.test.ts b/tests/integration/tools/mongodb/create/insertMany.test.ts index b4042029..eb8e1ef4 100644 --- a/tests/integration/tools/mongodb/create/insertMany.test.ts +++ b/tests/integration/tools/mongodb/create/insertMany.test.ts @@ -5,6 +5,7 @@ import { databaseCollectionParameters, validateToolMetadata, validateThrowsForInvalidArguments, + expectDefined, } from "../../../helpers.js"; describeWithMongoDB("insertMany tool", (integration) => { @@ -29,7 +30,7 @@ describeWithMongoDB("insertMany tool", (integration) => { const validateDocuments = async (collection: string, expectedDocuments: object[]) => { const collections = await integration.mongoClient().db(integration.randomDbName()).listCollections().toArray(); - expect(collections.find((c) => c.name === collection)).toBeDefined(); + expectDefined(collections.find((c) => c.name === collection)); const docs = await integration .mongoClient() @@ -81,7 +82,7 @@ describeWithMongoDB("insertMany tool", (integration) => { const content = getResponseContent(response.content); expect(content).toContain("Error running insert-many"); expect(content).toContain("duplicate key error"); - expect(content).toContain(insertedIds[0].toString()); + expect(content).toContain(insertedIds[0]?.toString()); }); validateAutoConnectBehavior(integration, "insert-many", () => { diff --git a/tests/integration/tools/mongodb/delete/dropCollection.test.ts b/tests/integration/tools/mongodb/delete/dropCollection.test.ts index 1dcaa218..48707156 100644 --- a/tests/integration/tools/mongodb/delete/dropCollection.test.ts +++ b/tests/integration/tools/mongodb/delete/dropCollection.test.ts @@ -54,7 +54,7 @@ describeWithMongoDB("dropCollection tool", (integration) => { ); const collections = await integration.mongoClient().db(integration.randomDbName()).listCollections().toArray(); expect(collections).toHaveLength(1); - expect(collections[0].name).toBe("coll2"); + expect(collections[0]?.name).toBe("coll2"); }); validateAutoConnectBehavior(integration, "drop-collection", () => { diff --git a/tests/integration/tools/mongodb/delete/dropDatabase.test.ts b/tests/integration/tools/mongodb/delete/dropDatabase.test.ts index 29a79206..47fa294c 100644 --- a/tests/integration/tools/mongodb/delete/dropDatabase.test.ts +++ b/tests/integration/tools/mongodb/delete/dropDatabase.test.ts @@ -6,6 +6,7 @@ import { validateThrowsForInvalidArguments, databaseParameters, databaseInvalidArgs, + expectDefined, } from "../../../helpers.js"; describeWithMongoDB("dropDatabase tool", (integration) => { @@ -21,7 +22,7 @@ describeWithMongoDB("dropDatabase tool", (integration) => { it("can drop non-existing database", async () => { let { databases } = await integration.mongoClient().db("").admin().listDatabases(); - const preDropLength = databases.length; + expect(databases.find((db) => db.name === integration.randomDbName())).toBeUndefined(); await integration.connectMcpClient(); const response = await integration.mcpClient().callTool({ @@ -36,7 +37,6 @@ describeWithMongoDB("dropDatabase tool", (integration) => { ({ databases } = await integration.mongoClient().db("").admin().listDatabases()); - expect(databases).toHaveLength(preDropLength); expect(databases.find((db) => db.name === integration.randomDbName())).toBeUndefined(); }); @@ -46,7 +46,7 @@ describeWithMongoDB("dropDatabase tool", (integration) => { await integration.mongoClient().db(integration.randomDbName()).createCollection("coll2"); let { databases } = await integration.mongoClient().db("").admin().listDatabases(); - expect(databases.find((db) => db.name === integration.randomDbName())).toBeDefined(); + expectDefined(databases.find((db) => db.name === integration.randomDbName())); const response = await integration.mcpClient().callTool({ name: "drop-database", diff --git a/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts b/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts index ccfc988f..e67b0ce5 100644 --- a/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts +++ b/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts @@ -56,7 +56,8 @@ describeWithMongoDB("collectionSchema tool", (integration) => { types: [{ bsonType: "String" }], }, age: { - types: [{ bsonType: "Number" as any }], + //@ts-expect-error This is a workaround + types: [{ bsonType: "Number" }], }, }, }, @@ -76,7 +77,8 @@ describeWithMongoDB("collectionSchema tool", (integration) => { types: [{ bsonType: "String" }], }, age: { - types: [{ bsonType: "Number" as any }, { bsonType: "String" }], + // @ts-expect-error This is a workaround + types: [{ bsonType: "Number" }, { bsonType: "String" }], }, country: { types: [{ bsonType: "String" }, { bsonType: "Boolean" }], @@ -109,7 +111,8 @@ describeWithMongoDB("collectionSchema tool", (integration) => { ], }, ageRange: { - types: [{ bsonType: "Array", types: [{ bsonType: "Number" as any }] }, { bsonType: "String" }], + // @ts-expect-error This is a workaround + types: [{ bsonType: "Array", types: [{ bsonType: "Number" }] }, { bsonType: "String" }], }, }, }, @@ -129,11 +132,11 @@ describeWithMongoDB("collectionSchema tool", (integration) => { expect(items).toHaveLength(2); // Expect to find _id, name, age - expect(items[0].text).toEqual( + expect(items[0]?.text).toEqual( `Found ${Object.entries(testCase.expectedSchema).length} fields in the schema for "${integration.randomDbName()}.foo"` ); - const schema = JSON.parse(items[1].text) as SimplifiedSchema; + const schema = JSON.parse(items[1]?.text ?? "{}") as SimplifiedSchema; expect(schema).toEqual(testCase.expectedSchema); }); } diff --git a/tests/integration/tools/mongodb/metadata/collectionStorageSize.test.ts b/tests/integration/tools/mongodb/metadata/collectionStorageSize.test.ts index 23e86cde..d8ffafbd 100644 --- a/tests/integration/tools/mongodb/metadata/collectionStorageSize.test.ts +++ b/tests/integration/tools/mongodb/metadata/collectionStorageSize.test.ts @@ -6,6 +6,7 @@ import { databaseCollectionInvalidArgs, validateToolMetadata, validateThrowsForInvalidArguments, + expectDefined, } from "../../../helpers.js"; import * as crypto from "crypto"; @@ -65,8 +66,8 @@ describeWithMongoDB("collectionStorageSize tool", (integration) => { expect(content).toContain(`The size of "${integration.randomDbName()}.foo" is`); const size = /is `(\d+\.\d+) ([a-zA-Z]*)`/.exec(content); - expect(size?.[1]).toBeDefined(); - expect(size?.[2]).toBeDefined(); + expectDefined(size?.[1]); + expectDefined(size?.[2]); expect(parseFloat(size?.[1] || "")).toBeGreaterThan(0); expect(size?.[2]).toBe(test.expectedScale); }); diff --git a/tests/integration/tools/mongodb/metadata/connect.test.ts b/tests/integration/tools/mongodb/metadata/connect.test.ts index 017c6779..47e91d13 100644 --- a/tests/integration/tools/mongodb/metadata/connect.test.ts +++ b/tests/integration/tools/mongodb/metadata/connect.test.ts @@ -1,130 +1,128 @@ import { describeWithMongoDB } from "../mongodbHelpers.js"; - -import { getResponseContent, validateToolMetadata } from "../../../helpers.js"; - +import { getResponseContent, validateThrowsForInvalidArguments, validateToolMetadata } from "../../../helpers.js"; import { config } from "../../../../../src/config.js"; -describeWithMongoDB("Connect tool", (integration) => { - validateToolMetadata(integration, "connect", "Connect to a MongoDB instance", [ - { - name: "options", - description: - "Options for connecting to MongoDB. If not provided, the connection string from the config://connection-string resource will be used. If the user hasn't specified Atlas cluster name or a connection string explicitly and the `config://connection-string` resource is present, always invoke this with no arguments.", - type: "array", - required: false, - }, - ]); - - describe("with default config", () => { - describe("without connection string", () => { - it("prompts for connection string", async () => { - const response = await integration.mcpClient().callTool({ name: "connect", arguments: {} }); - const content = getResponseContent(response.content); - expect(content).toContain("No connection details provided"); - }); +describeWithMongoDB( + "switchConnection tool", + (integration) => { + beforeEach(() => { + integration.mcpServer().userConfig.connectionString = integration.connectionString(); }); - describe("with connection string", () => { + validateToolMetadata( + integration, + "switch-connection", + "Switch to a different MongoDB connection. If the user has configured a connection string or has previously called the connect tool, a connection is already established and there's no need to call this tool unless the user has explicitly requested to switch to a new instance.", + [ + { + name: "connectionString", + description: "MongoDB connection string to switch to (in the mongodb:// or mongodb+srv:// format)", + type: "string", + required: false, + }, + ] + ); + + validateThrowsForInvalidArguments(integration, "switch-connection", [{ connectionString: 123 }]); + + describe("without arguments", () => { it("connects to the database", async () => { - const response = await integration.mcpClient().callTool({ - name: "connect", - arguments: { - options: [ - { - connectionString: integration.connectionString(), - }, - ], - }, - }); + const response = await integration.mcpClient().callTool({ name: "switch-connection" }); const content = getResponseContent(response.content); expect(content).toContain("Successfully connected"); - expect(content).toContain(integration.connectionString()); }); }); - describe("with invalid connection string", () => { - it("returns error message", async () => { - const response = await integration.mcpClient().callTool({ - name: "connect", - arguments: { options: [{ connectionString: "mongodb://localhost:12345" }] }, - }); - const content = getResponseContent(response.content); - expect(content).toContain("Error running connect"); - - // Should not suggest using the config connection string (because we don't have one) - expect(content).not.toContain("Your config lists a different connection string"); - }); + it("doesn't have the connect tool registered", async () => { + const { tools } = await integration.mcpClient().listTools(); + const tool = tools.find((tool) => tool.name === "connect"); + expect(tool).toBeUndefined(); }); - }); - describe("with connection string in config", () => { - beforeEach(async () => { - config.connectionString = integration.connectionString(); - }); - - it("uses the connection string from config", async () => { - const response = await integration.mcpClient().callTool({ name: "connect", arguments: {} }); + it("defaults to the connection string from config", async () => { + const response = await integration.mcpClient().callTool({ name: "switch-connection", arguments: {} }); const content = getResponseContent(response.content); expect(content).toContain("Successfully connected"); - expect(content).toContain(integration.connectionString()); }); - it("prefers connection string from arguments", async () => { + it("switches to the connection string from the arguments", async () => { const newConnectionString = `${integration.connectionString()}?appName=foo-bar`; const response = await integration.mcpClient().callTool({ - name: "connect", + name: "switch-connection", arguments: { - options: [ - { - connectionString: newConnectionString, - }, - ], + connectionString: newConnectionString, }, }); const content = getResponseContent(response.content); expect(content).toContain("Successfully connected"); - expect(content).toContain(newConnectionString); }); describe("when the arugment connection string is invalid", () => { - it("suggests the config connection string if set", async () => { + it("returns error message", async () => { const response = await integration.mcpClient().callTool({ - name: "connect", + name: "switch-connection", arguments: { - options: [ - { - connectionString: "mongodb://localhost:12345", - }, - ], + connectionString: "mongodb://localhost:12345", }, }); + const content = getResponseContent(response.content); - expect(content).toContain("Failed to connect to MongoDB at 'mongodb://localhost:12345'"); - expect(content).toContain( - `Your config lists a different connection string: '${config.connectionString}' - do you want to try connecting to it instead?` - ); + + expect(content).toContain("Error running switch-connection"); }); + }); + }, + (mdbIntegration) => ({ + ...config, + connectionString: mdbIntegration.connectionString(), + }) +); +describeWithMongoDB( + "Connect tool", + (integration) => { + validateToolMetadata(integration, "connect", "Connect to a MongoDB instance", [ + { + name: "connectionString", + description: "MongoDB connection string (in the mongodb:// or mongodb+srv:// format)", + type: "string", + required: true, + }, + ]); + + validateThrowsForInvalidArguments(integration, "connect", [{}, { connectionString: 123 }]); + + it("doesn't have the switch-connection tool registered", async () => { + const { tools } = await integration.mcpClient().listTools(); + const tool = tools.find((tool) => tool.name === "switch-connection"); + expect(tool).toBeUndefined(); + }); - it("returns error message if the config connection string matches the argument", async () => { - config.connectionString = "mongodb://localhost:12345"; + describe("with connection string", () => { + it("connects to the database", async () => { const response = await integration.mcpClient().callTool({ name: "connect", arguments: { - options: [ - { - connectionString: "mongodb://localhost:12345", - }, - ], + connectionString: integration.connectionString(), }, }); - const content = getResponseContent(response.content); + expect(content).toContain("Successfully connected"); + }); + }); - // Should be handled by default error handler and not suggest the config connection string - // because it matches the argument connection string + describe("with invalid connection string", () => { + it("returns error message", async () => { + const response = await integration.mcpClient().callTool({ + name: "connect", + arguments: { connectionString: "mongodb://localhost:12345" }, + }); + const content = getResponseContent(response.content); expect(content).toContain("Error running connect"); + + // Should not suggest using the config connection string (because we don't have one) expect(content).not.toContain("Your config lists a different connection string"); }); }); - }); -}); + }, + () => config +); diff --git a/tests/integration/tools/mongodb/metadata/dbStats.test.ts b/tests/integration/tools/mongodb/metadata/dbStats.test.ts index 8e4a57c7..2ce4a84c 100644 --- a/tests/integration/tools/mongodb/metadata/dbStats.test.ts +++ b/tests/integration/tools/mongodb/metadata/dbStats.test.ts @@ -28,9 +28,13 @@ describeWithMongoDB("dbStats tool", (integration) => { }); const elements = getResponseElements(response.content); expect(elements).toHaveLength(2); - expect(elements[0].text).toBe(`Statistics for database ${integration.randomDbName()}`); + expect(elements[0]?.text).toBe(`Statistics for database ${integration.randomDbName()}`); - const stats = JSON.parse(elements[1].text); + const stats = JSON.parse(elements[1]?.text ?? "{}") as { + db: string; + collections: number; + storageSize: number; + }; expect(stats.db).toBe(integration.randomDbName()); expect(stats.collections).toBe(0); expect(stats.storageSize).toBe(0); @@ -71,26 +75,30 @@ describeWithMongoDB("dbStats tool", (integration) => { }); const elements = getResponseElements(response.content); expect(elements).toHaveLength(2); - expect(elements[0].text).toBe(`Statistics for database ${integration.randomDbName()}`); + expect(elements[0]?.text).toBe(`Statistics for database ${integration.randomDbName()}`); - const stats = JSON.parse(elements[1].text); + const stats = JSON.parse(elements[1]?.text ?? "{}") as { + db: string; + collections: unknown; + storageSize: unknown; + objects: unknown; + }; expect(stats.db).toBe(integration.randomDbName()); expect(stats.collections).toBe(Object.entries(test.collections).length); expect(stats.storageSize).toBeGreaterThan(1024); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return expect(stats.objects).toBe(Object.values(test.collections).reduce((a, b) => a + b, 0)); }); } }); - describe("when not connected", () => { - validateAutoConnectBehavior(integration, "db-stats", () => { - return { - args: { - database: integration.randomDbName(), - collection: "foo", - }, - expectedResponse: `Statistics for database ${integration.randomDbName()}`, - }; - }); + validateAutoConnectBehavior(integration, "db-stats", () => { + return { + args: { + database: integration.randomDbName(), + collection: "foo", + }, + expectedResponse: `Statistics for database ${integration.randomDbName()}`, + }; }); }); diff --git a/tests/integration/tools/mongodb/metadata/explain.test.ts b/tests/integration/tools/mongodb/metadata/explain.test.ts index dafdd238..44cfe6ff 100644 --- a/tests/integration/tools/mongodb/metadata/explain.test.ts +++ b/tests/integration/tools/mongodb/metadata/explain.test.ts @@ -1,6 +1,5 @@ import { databaseCollectionParameters, - setupIntegrationTest, validateToolMetadata, validateThrowsForInvalidArguments, getResponseElements, @@ -90,12 +89,12 @@ describeWithMongoDB("explain tool", (integration) => { const content = getResponseElements(response.content); expect(content).toHaveLength(2); - expect(content[0].text).toEqual( + expect(content[0]?.text).toEqual( `Here is some information about the winning plan chosen by the query optimizer for running the given \`${testCase.method}\` operation in "${integration.randomDbName()}.coll1". This information can be used to understand how the query was executed and to optimize the query performance.` ); - expect(content[1].text).toContain("queryPlanner"); - expect(content[1].text).toContain("winningPlan"); + expect(content[1]?.text).toContain("queryPlanner"); + expect(content[1]?.text).toContain("winningPlan"); }); } }); @@ -140,22 +139,22 @@ describeWithMongoDB("explain tool", (integration) => { const content = getResponseElements(response.content); expect(content).toHaveLength(2); - expect(content[0].text).toEqual( + expect(content[0]?.text).toEqual( `Here is some information about the winning plan chosen by the query optimizer for running the given \`${testCase.method}\` operation in "${integration.randomDbName()}.people". This information can be used to understand how the query was executed and to optimize the query performance.` ); - expect(content[1].text).toContain("queryPlanner"); - expect(content[1].text).toContain("winningPlan"); + expect(content[1]?.text).toContain("queryPlanner"); + expect(content[1]?.text).toContain("winningPlan"); if (indexed) { if (testCase.method === "count") { - expect(content[1].text).toContain("COUNT_SCAN"); + expect(content[1]?.text).toContain("COUNT_SCAN"); } else { - expect(content[1].text).toContain("IXSCAN"); + expect(content[1]?.text).toContain("IXSCAN"); } - expect(content[1].text).toContain("name_1"); + expect(content[1]?.text).toContain("name_1"); } else { - expect(content[1].text).toContain("COLLSCAN"); + expect(content[1]?.text).toContain("COLLSCAN"); } }); } diff --git a/tests/integration/tools/mongodb/metadata/listCollections.test.ts b/tests/integration/tools/mongodb/metadata/listCollections.test.ts index cef0a59d..c975d8de 100644 --- a/tests/integration/tools/mongodb/metadata/listCollections.test.ts +++ b/tests/integration/tools/mongodb/metadata/listCollections.test.ts @@ -45,7 +45,7 @@ describeWithMongoDB("listCollections tool", (integration) => { }); const items = getResponseElements(response.content); expect(items).toHaveLength(1); - expect(items[0].text).toContain('Name: "collection-1"'); + expect(items[0]?.text).toContain('Name: "collection-1"'); await mongoClient.db(integration.randomDbName()).createCollection("collection-2"); diff --git a/tests/integration/tools/mongodb/metadata/listDatabases.test.ts b/tests/integration/tools/mongodb/metadata/listDatabases.test.ts index 3288cf30..b828d743 100644 --- a/tests/integration/tools/mongodb/metadata/listDatabases.test.ts +++ b/tests/integration/tools/mongodb/metadata/listDatabases.test.ts @@ -1,13 +1,13 @@ import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js"; -import { getResponseElements, getParameters } from "../../../helpers.js"; +import { getResponseElements, getParameters, expectDefined } from "../../../helpers.js"; describeWithMongoDB("listDatabases tool", (integration) => { const defaultDatabases = ["admin", "config", "local"]; it("should have correct metadata", async () => { const { tools } = await integration.mcpClient().listTools(); - const listDatabases = tools.find((tool) => tool.name === "list-databases")!; - expect(listDatabases).toBeDefined(); + const listDatabases = tools.find((tool) => tool.name === "list-databases"); + expectDefined(listDatabases); expect(listDatabases.description).toBe("List all databases for a MongoDB connection"); const parameters = getParameters(listDatabases); @@ -54,7 +54,7 @@ describeWithMongoDB("listDatabases tool", (integration) => { async () => { const mongoClient = integration.mongoClient(); const { databases } = await mongoClient.db("admin").command({ listDatabases: 1, nameOnly: true }); - for (const db of databases) { + for (const db of databases as { name: string }[]) { if (!defaultDatabases.includes(db.name)) { await mongoClient.db(db.name).dropDatabase(); } @@ -65,9 +65,13 @@ describeWithMongoDB("listDatabases tool", (integration) => { function getDbNames(content: unknown): (string | null)[] { const responseItems = getResponseElements(content); - - return responseItems.map((item) => { - const match = item.text.match(/Name: (.*), Size: \d+ bytes/); - return match ? match[1] : null; - }); + return responseItems + .map((item) => { + if (item && typeof item.text === "string") { + const match = item.text.match(/Name: ([^,]+), Size: \d+ bytes/); + return match ? match[1] : null; + } + return null; + }) + .filter((item): item is string | null => item !== undefined); } diff --git a/tests/integration/tools/mongodb/metadata/logs.test.ts b/tests/integration/tools/mongodb/metadata/logs.test.ts new file mode 100644 index 00000000..debbb1ae --- /dev/null +++ b/tests/integration/tools/mongodb/metadata/logs.test.ts @@ -0,0 +1,83 @@ +import { validateToolMetadata, validateThrowsForInvalidArguments, getResponseElements } from "../../../helpers.js"; +import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js"; + +describeWithMongoDB("logs tool", (integration) => { + validateToolMetadata(integration, "mongodb-logs", "Returns the most recent logged mongod events", [ + { + type: "string", + name: "type", + description: + "The type of logs to return. Global returns all recent log entries, while startupWarnings returns only warnings and errors from when the process started.", + required: false, + }, + { + type: "integer", + name: "limit", + description: "The maximum number of log entries to return.", + required: false, + }, + ]); + + validateThrowsForInvalidArguments(integration, "mongodb-logs", [ + { type: 123 }, + { type: "something" }, + { limit: 0 }, + { limit: true }, + { limit: 1025 }, + ]); + + it("should return global logs", async () => { + await integration.connectMcpClient(); + const response = await integration.mcpClient().callTool({ + name: "mongodb-logs", + arguments: {}, + }); + + const elements = getResponseElements(response); + + // Default limit is 50 + expect(elements.length).toBeLessThanOrEqual(51); + expect(elements[0]?.text).toMatch(/Found: \d+ messages/); + + for (let i = 1; i < elements.length; i++) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const log = JSON.parse(elements[i]?.text ?? "{}"); + expect(log).toHaveProperty("t"); + expect(log).toHaveProperty("msg"); + } + }); + + it("should return startupWarnings logs", async () => { + await integration.connectMcpClient(); + const response = await integration.mcpClient().callTool({ + name: "mongodb-logs", + arguments: { + type: "startupWarnings", + }, + }); + + 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(log).toHaveProperty("t"); + expect(log).toHaveProperty("msg"); + expect(log).toHaveProperty("tags"); + expect(log.tags).toContain("startupWarnings"); + } + }); + + validateAutoConnectBehavior(integration, "mongodb-logs", () => { + return { + args: { + database: integration.randomDbName(), + collection: "foo", + }, + validate: (content) => { + const elements = getResponseElements(content); + expect(elements.length).toBeLessThanOrEqual(51); + expect(elements[0]?.text).toMatch(/Found: \d+ messages/); + }, + }; + }); +}); diff --git a/tests/integration/tools/mongodb/mongodbHelpers.ts b/tests/integration/tools/mongodb/mongodbHelpers.ts index 44584339..ca4b09c1 100644 --- a/tests/integration/tools/mongodb/mongodbHelpers.ts +++ b/tests/integration/tools/mongodb/mongodbHelpers.ts @@ -1,46 +1,56 @@ -import runner, { MongoCluster } from "mongodb-runner"; +import { MongoCluster } from "mongodb-runner"; import path from "path"; +import { fileURLToPath } from "url"; import fs from "fs/promises"; import { MongoClient, ObjectId } from "mongodb"; -import { getResponseContent, IntegrationTest, setupIntegrationTest } from "../../helpers.js"; -import { UserConfig, config } from "../../../../src/config.js"; +import { getResponseContent, IntegrationTest, setupIntegrationTest, defaultTestConfig } from "../../helpers.js"; +import { UserConfig } from "../../../../src/config.js"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); interface MongoDBIntegrationTest { mongoClient: () => MongoClient; connectionString: () => string; - connectMcpClient: () => Promise; randomDbName: () => string; } export function describeWithMongoDB( - name: number | string | Function | jest.FunctionLike, - fn: (integration: IntegrationTest & MongoDBIntegrationTest) => void -): void { - describe("mongodb", () => { - const integration = setupIntegrationTest(); - const mdbIntegration = setupMongoDBIntegrationTest(integration); - describe(name, () => { - fn({ ...integration, ...mdbIntegration }); + name: string, + fn: (integration: IntegrationTest & MongoDBIntegrationTest & { connectMcpClient: () => Promise }) => void, + getUserConfig: (mdbIntegration: MongoDBIntegrationTest) => UserConfig = () => defaultTestConfig +) { + describe(name, () => { + const mdbIntegration = setupMongoDBIntegrationTest(); + const integration = setupIntegrationTest(() => ({ + ...getUserConfig(mdbIntegration), + })); + + fn({ + ...integration, + ...mdbIntegration, + connectMcpClient: async () => { + const { tools } = await integration.mcpClient().listTools(); + if (tools.find((tool) => tool.name === "connect")) { + await integration.mcpClient().callTool({ + name: "connect", + arguments: { connectionString: mdbIntegration.connectionString() }, + }); + } + }, }); }); } -export function setupMongoDBIntegrationTest( - integration: IntegrationTest, - userConfig: UserConfig = config -): MongoDBIntegrationTest { - let mongoCluster: runner.MongoCluster | undefined; +export function setupMongoDBIntegrationTest(): MongoDBIntegrationTest { + let mongoCluster: MongoCluster | undefined; let mongoClient: MongoClient | undefined; let randomDbName: string; - beforeEach(async () => { + beforeEach(() => { randomDbName = new ObjectId().toString(); }); afterEach(async () => { - await integration.mcpServer().session.close(); - config.connectionString = undefined; - await mongoClient?.close(); mongoClient = undefined; }); @@ -66,11 +76,13 @@ export function setupMongoDBIntegrationTest( } catch (err) { if (i < 5) { // Just wait a little bit and retry + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions console.error(`Failed to start cluster in ${dbsDir}, attempt ${i}: ${err}`); await new Promise((resolve) => setTimeout(resolve, 1000)); } else { // If we still fail after 5 seconds, try another db dir console.error( + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `Failed to start cluster in ${dbsDir}, attempt ${i}: ${err}. Retrying with a new db dir.` ); dbsDir = path.join(tmpDir, "mongodb-runner", `dbs${i - 5}`); @@ -102,12 +114,7 @@ export function setupMongoDBIntegrationTest( return mongoClient; }, connectionString: getConnectionString, - connectMcpClient: async () => { - await integration.mcpClient().callTool({ - name: "connect", - arguments: { options: [{ connectionString: getConnectionString() }] }, - }); - }, + randomDbName: () => randomDbName, }; } @@ -127,8 +134,12 @@ export function validateAutoConnectBehavior( beforeEach(() => beforeEachImpl()); } + afterEach(() => { + integration.mcpServer().userConfig.connectionString = undefined; + }); + it("connects automatically if connection string is configured", async () => { - config.connectionString = integration.connectionString(); + integration.mcpServer().userConfig.connectionString = integration.connectionString(); const validationInfo = validation(); diff --git a/tests/integration/tools/mongodb/read/aggregate.test.ts b/tests/integration/tools/mongodb/read/aggregate.test.ts index 148117e1..6cf6aff0 100644 --- a/tests/integration/tools/mongodb/read/aggregate.test.ts +++ b/tests/integration/tools/mongodb/read/aggregate.test.ts @@ -22,7 +22,6 @@ describeWithMongoDB("aggregate tool", (integration) => { { database: "test", collection: "foo" }, { database: test, pipeline: [] }, { database: "test", collection: "foo", pipeline: {} }, - { database: "test", collection: "foo", pipeline: [], extra: "extra" }, { database: "test", collection: [], pipeline: [] }, { database: 123, collection: "foo", pipeline: [] }, ]); @@ -36,7 +35,7 @@ describeWithMongoDB("aggregate tool", (integration) => { const elements = getResponseElements(response.content); expect(elements).toHaveLength(1); - expect(elements[0].text).toEqual('Found 0 documents in the collection "people":'); + expect(elements[0]?.text).toEqual('Found 0 documents in the collection "people":'); }); it("can run aggragation on an empty collection", async () => { @@ -54,7 +53,7 @@ describeWithMongoDB("aggregate tool", (integration) => { const elements = getResponseElements(response.content); expect(elements).toHaveLength(1); - expect(elements[0].text).toEqual('Found 0 documents in the collection "people":'); + expect(elements[0]?.text).toEqual('Found 0 documents in the collection "people":'); }); it("can run aggragation on an existing collection", async () => { @@ -80,9 +79,21 @@ describeWithMongoDB("aggregate tool", (integration) => { const elements = getResponseElements(response.content); expect(elements).toHaveLength(3); - expect(elements[0].text).toEqual('Found 2 documents in the collection "people":'); - expect(JSON.parse(elements[1].text)).toEqual({ _id: expect.any(Object), name: "Søren", age: 15 }); - expect(JSON.parse(elements[2].text)).toEqual({ _id: expect.any(Object), name: "Laura", age: 10 }); + expect(elements[0]?.text).toEqual('Found 2 documents in the collection "people":'); + expect(asObject(JSON.parse(elements[1]?.text ?? "{}"))).toEqual( + expect.objectContaining({ + _id: expect.any(Object) as object, + name: "Søren", + age: 15, + }) + ); + expect(asObject(JSON.parse(elements[2]?.text ?? "{}"))).toEqual( + expect.objectContaining({ + _id: expect.any(Object) as object, + name: "Laura", + age: 10, + }) + ); }); validateAutoConnectBehavior(integration, "aggregate", () => { @@ -96,3 +107,8 @@ describeWithMongoDB("aggregate tool", (integration) => { }; }); }); + +function asObject(val: unknown): Record { + if (typeof val === "object" && val !== null) return val as Record; + throw new Error("Expected an object"); +} diff --git a/tests/integration/tools/mongodb/read/collectionIndexes.test.ts b/tests/integration/tools/mongodb/read/collectionIndexes.test.ts index 2e919080..5902cfb7 100644 --- a/tests/integration/tools/mongodb/read/collectionIndexes.test.ts +++ b/tests/integration/tools/mongodb/read/collectionIndexes.test.ts @@ -5,6 +5,7 @@ import { validateThrowsForInvalidArguments, getResponseElements, databaseCollectionInvalidArgs, + expectDefined, } from "../../../helpers.js"; import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js"; @@ -27,7 +28,7 @@ describeWithMongoDB("collectionIndexes tool", (integration) => { const elements = getResponseElements(response.content); expect(elements).toHaveLength(1); - expect(elements[0].text).toEqual( + expect(elements[0]?.text).toEqual( 'The indexes for "non-existent.people" cannot be determined because the collection does not exist.' ); }); @@ -46,8 +47,8 @@ describeWithMongoDB("collectionIndexes tool", (integration) => { const elements = getResponseElements(response.content); expect(elements).toHaveLength(2); - expect(elements[0].text).toEqual('Found 1 indexes in the collection "people":'); - expect(elements[1].text).toEqual('Name "_id_", definition: {"_id":1}'); + expect(elements[0]?.text).toEqual('Found 1 indexes in the collection "people":'); + expect(elements[1]?.text).toEqual('Name "_id_", definition: {"_id":1}'); }); it("returns all indexes for a collection", async () => { @@ -73,19 +74,19 @@ describeWithMongoDB("collectionIndexes tool", (integration) => { const elements = getResponseElements(response.content); expect(elements).toHaveLength(indexTypes.length + 2); - expect(elements[0].text).toEqual(`Found ${indexTypes.length + 1} indexes in the collection "people":`); - expect(elements[1].text).toEqual('Name "_id_", definition: {"_id":1}'); + expect(elements[0]?.text).toEqual(`Found ${indexTypes.length + 1} indexes in the collection "people":`); + expect(elements[1]?.text).toEqual('Name "_id_", definition: {"_id":1}'); for (const indexType of indexTypes) { const index = elements.find((element) => element.text.includes(`prop_${indexType}`)); - expect(index).toBeDefined(); + expectDefined(index); let expectedDefinition = JSON.stringify({ [`prop_${indexType}`]: indexType }); if (indexType === "text") { expectedDefinition = '{"_fts":"text"'; } - expect(index!.text).toContain(`definition: ${expectedDefinition}`); + expect(index.text).toContain(`definition: ${expectedDefinition}`); } }); diff --git a/tests/integration/tools/mongodb/read/count.test.ts b/tests/integration/tools/mongodb/read/count.test.ts index 938285a8..39b88607 100644 --- a/tests/integration/tools/mongodb/read/count.test.ts +++ b/tests/integration/tools/mongodb/read/count.test.ts @@ -8,21 +8,25 @@ import { } from "../../../helpers.js"; describeWithMongoDB("count tool", (integration) => { - validateToolMetadata(integration, "count", "Gets the number of documents in a MongoDB collection", [ - { - name: "query", - description: - "The query filter to count documents. Matches the syntax of the filter argument of db.collection.count()", - type: "object", - required: false, - }, - ...databaseCollectionParameters, - ]); + validateToolMetadata( + integration, + "count", + "Gets the number of documents in a MongoDB collection using db.collection.count() and query as an optional filter parameter", + [ + { + name: "query", + description: + "A filter/query parameter. Allows users to filter the documents to count. Matches the syntax of the filter argument of db.collection.count().", + type: "object", + required: false, + }, + ...databaseCollectionParameters, + ] + ); validateThrowsForInvalidArguments(integration, "count", [ {}, { database: 123, collection: "bar" }, - { foo: "bar", database: "test", collection: "bar" }, { collection: [], database: "test" }, { collection: "bar", database: "test", query: "{ $gt: { foo: 5 } }" }, ]); diff --git a/tests/integration/tools/mongodb/read/find.test.ts b/tests/integration/tools/mongodb/read/find.test.ts index f2a3cfc3..209ed1a5 100644 --- a/tests/integration/tools/mongodb/read/find.test.ts +++ b/tests/integration/tools/mongodb/read/find.test.ts @@ -1,10 +1,10 @@ import { getResponseContent, databaseCollectionParameters, - setupIntegrationTest, validateToolMetadata, validateThrowsForInvalidArguments, getResponseElements, + expectDefined, } from "../../../helpers.js"; import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js"; @@ -42,7 +42,6 @@ describeWithMongoDB("find tool", (integration) => { validateThrowsForInvalidArguments(integration, "find", [ {}, { database: 123, collection: "bar" }, - { database: "test", collection: "bar", extra: "extra" }, { database: "test", collection: [] }, { database: "test", collection: "bar", filter: "{ $gt: { foo: 5 } }" }, { database: "test", collection: "bar", projection: "name" }, @@ -86,24 +85,25 @@ describeWithMongoDB("find tool", (integration) => { const testCases: { name: string; - filter?: any; + filter?: unknown; limit?: number; - projection?: any; - sort?: any; - expected: any[]; + projection?: unknown; + sort?: unknown; + expected: unknown[]; }[] = [ { name: "returns all documents when no filter is provided", expected: Array(10) .fill(0) - .map((_, index) => ({ _id: expect.any(Object), value: index })), + .map((_, index) => ({ _id: expect.any(Object) as unknown, value: index })), }, { name: "returns documents matching the filter", filter: { value: { $gt: 5 } }, expected: Array(4) .fill(0) - .map((_, index) => ({ _id: expect.any(Object), value: index + 6 })), + + .map((_, index) => ({ _id: expect.any(Object) as unknown, value: index + 6 })), }, { name: "returns documents matching the filter with projection", @@ -118,8 +118,8 @@ describeWithMongoDB("find tool", (integration) => { filter: { value: { $gt: 5 } }, limit: 2, expected: [ - { _id: expect.any(Object), value: 6 }, - { _id: expect.any(Object), value: 7 }, + { _id: expect.any(Object) as unknown, value: 6 }, + { _id: expect.any(Object) as unknown, value: 7 }, ], }, { @@ -128,7 +128,7 @@ describeWithMongoDB("find tool", (integration) => { sort: { value: -1 }, expected: Array(10) .fill(0) - .map((_, index) => ({ _id: expect.any(Object), value: index })) + .map((_, index) => ({ _id: expect.any(Object) as unknown, value: index })) .reverse(), }, ]; @@ -149,10 +149,10 @@ describeWithMongoDB("find tool", (integration) => { }); const elements = getResponseElements(response.content); expect(elements).toHaveLength(expected.length + 1); - expect(elements[0].text).toEqual(`Found ${expected.length} documents in the collection "foo":`); + expect(elements[0]?.text).toEqual(`Found ${expected.length} documents in the collection "foo":`); for (let i = 0; i < expected.length; i++) { - expect(JSON.parse(elements[i + 1].text)).toEqual(expected[i]); + expect(JSON.parse(elements[i + 1]?.text ?? "{}")).toEqual(expected[i]); } }); } @@ -165,12 +165,40 @@ describeWithMongoDB("find tool", (integration) => { }); const elements = getResponseElements(response.content); expect(elements).toHaveLength(11); - expect(elements[0].text).toEqual('Found 10 documents in the collection "foo":'); + expect(elements[0]?.text).toEqual('Found 10 documents in the collection "foo":'); for (let i = 0; i < 10; i++) { - expect(JSON.parse(elements[i + 1].text).value).toEqual(i); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(JSON.parse(elements[i + 1]?.text ?? "{}").value).toEqual(i); } }); + + it("can find objects by $oid", async () => { + await integration.connectMcpClient(); + + const fooObject = await integration + .mongoClient() + .db(integration.randomDbName()) + .collection("foo") + .findOne(); + expectDefined(fooObject); + + const response = await integration.mcpClient().callTool({ + name: "find", + arguments: { + database: integration.randomDbName(), + collection: "foo", + filter: { _id: fooObject._id }, + }, + }); + + const elements = getResponseElements(response.content); + expect(elements).toHaveLength(2); + expect(elements[0]?.text).toEqual('Found 1 documents in the collection "foo":'); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(JSON.parse(elements[1]?.text ?? "{}").value).toEqual(fooObject.value); + }); }); validateAutoConnectBehavior(integration, "find", () => { diff --git a/tests/integration/tools/mongodb/update/renameCollection.test.ts b/tests/integration/tools/mongodb/update/renameCollection.test.ts index 1c904458..6e7a4128 100644 --- a/tests/integration/tools/mongodb/update/renameCollection.test.ts +++ b/tests/integration/tools/mongodb/update/renameCollection.test.ts @@ -1,7 +1,6 @@ import { getResponseContent, databaseCollectionParameters, - setupIntegrationTest, validateToolMetadata, validateThrowsForInvalidArguments, } from "../../../helpers.js"; @@ -28,7 +27,6 @@ describeWithMongoDB("renameCollection tool", (integration) => { validateThrowsForInvalidArguments(integration, "rename-collection", [ {}, { database: 123, collection: "bar" }, - { database: "test", collection: "bar", newName: "foo", extra: "extra" }, { database: "test", collection: [], newName: "foo" }, { database: "test", collection: "bar", newName: 10 }, { database: "test", collection: "bar", newName: "foo", dropTarget: "true" }, @@ -96,7 +94,7 @@ describeWithMongoDB("renameCollection tool", (integration) => { .find({}) .toArray(); expect(docsInAfter).toHaveLength(1); - expect(docsInAfter[0].value).toEqual(42); + expect(docsInAfter[0]?.value).toEqual(42); }); it("returns an error when renaming to an existing collection", async () => { @@ -125,7 +123,7 @@ describeWithMongoDB("renameCollection tool", (integration) => { .find({}) .toArray(); expect(docsInBefore).toHaveLength(1); - expect(docsInBefore[0].value).toEqual(42); + expect(docsInBefore[0]?.value).toEqual(42); const docsInAfter = await integration .mongoClient() @@ -134,7 +132,7 @@ describeWithMongoDB("renameCollection tool", (integration) => { .find({}) .toArray(); expect(docsInAfter).toHaveLength(1); - expect(docsInAfter[0].value).toEqual(84); + expect(docsInAfter[0]?.value).toEqual(84); }); it("renames to existing collection with dropTarget", async () => { @@ -176,7 +174,7 @@ describeWithMongoDB("renameCollection tool", (integration) => { .find({}) .toArray(); expect(docsInAfter).toHaveLength(1); - expect(docsInAfter[0].value).toEqual(42); + expect(docsInAfter[0]?.value).toEqual(42); }); }); diff --git a/tests/integration/tools/mongodb/update/updateMany.test.ts b/tests/integration/tools/mongodb/update/updateMany.test.ts index 6a05f640..77840d95 100644 --- a/tests/integration/tools/mongodb/update/updateMany.test.ts +++ b/tests/integration/tools/mongodb/update/updateMany.test.ts @@ -42,7 +42,6 @@ describeWithMongoDB("updateMany tool", (integration) => { { database: 123, collection: "bar", update: {} }, { database: [], collection: "bar", update: {} }, { database: "test", collection: "bar", update: [] }, - { database: "test", collection: "bar", update: {}, extra: true }, { database: "test", collection: "bar", update: {}, filter: 123 }, { database: "test", collection: "bar", update: {}, upsert: "true" }, { database: "test", collection: "bar", update: {}, filter: {}, upsert: "true" }, diff --git a/tests/unit/EJsonTransport.test.ts b/tests/unit/EJsonTransport.test.ts new file mode 100644 index 00000000..6bbb7999 --- /dev/null +++ b/tests/unit/EJsonTransport.test.ts @@ -0,0 +1,71 @@ +import { Decimal128, MaxKey, MinKey, ObjectId, Timestamp, UUID } from "bson"; +import { createEJsonTransport, EJsonReadBuffer } from "../../src/helpers/EJsonTransport.js"; +import { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { Readable } from "stream"; +import { ReadBuffer } from "@modelcontextprotocol/sdk/shared/stdio.js"; + +describe("EJsonTransport", () => { + let transport: StdioServerTransport; + beforeEach(async () => { + transport = createEJsonTransport(); + await transport.start(); + }); + + afterEach(async () => { + await transport.close(); + }); + + it("ejson deserializes messages", () => { + const messages: { message: JSONRPCMessage; extra?: { authInfo?: AuthInfo } }[] = []; + transport.onmessage = ( + message, + extra?: { + authInfo?: AuthInfo; + } + ) => { + messages.push({ message, extra }); + }; + + (transport["_stdin"] as Readable).emit( + "data", + Buffer.from( + '{"jsonrpc":"2.0","id":1,"method":"testMethod","params":{"oid":{"$oid":"681b741f13aa74a0687b5110"},"uuid":{"$uuid":"f81d4fae-7dec-11d0-a765-00a0c91e6bf6"},"date":{"$date":"2025-05-07T14:54:23.973Z"},"decimal":{"$numberDecimal":"1234567890987654321"},"int32":123,"maxKey":{"$maxKey":1},"minKey":{"$minKey":1},"timestamp":{"$timestamp":{"t":123,"i":456}}}}\n', + "utf-8" + ) + ); + + expect(messages.length).toBe(1); + const message = messages[0]?.message; + + expect(message).toEqual({ + jsonrpc: "2.0", + id: 1, + method: "testMethod", + params: { + oid: new ObjectId("681b741f13aa74a0687b5110"), + uuid: new UUID("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"), + date: new Date(Date.parse("2025-05-07T14:54:23.973Z")), + decimal: new Decimal128("1234567890987654321"), + int32: 123, + maxKey: new MaxKey(), + minKey: new MinKey(), + timestamp: new Timestamp({ t: 123, i: 456 }), + }, + }); + }); + + it("has _readBuffer field of type EJsonReadBuffer", () => { + expect(transport["_readBuffer"]).toBeDefined(); + expect(transport["_readBuffer"]).toBeInstanceOf(EJsonReadBuffer); + }); + + describe("standard StdioServerTransport", () => { + it("has a _readBuffer field", () => { + const standardTransport = new StdioServerTransport(); + expect(standardTransport["_readBuffer"]).toBeDefined(); + expect(standardTransport["_readBuffer"]).toBeInstanceOf(ReadBuffer); + }); + }); +}); diff --git a/tests/unit/apiClient.test.ts b/tests/unit/apiClient.test.ts new file mode 100644 index 00000000..6b9fd427 --- /dev/null +++ b/tests/unit/apiClient.test.ts @@ -0,0 +1,193 @@ +import { jest } from "@jest/globals"; +import { ApiClient } from "../../src/common/atlas/apiClient.js"; +import { CommonProperties, TelemetryEvent, TelemetryResult } from "../../src/telemetry/types.js"; + +describe("ApiClient", () => { + let apiClient: ApiClient; + + const mockEvents: TelemetryEvent[] = [ + { + timestamp: new Date().toISOString(), + source: "mdbmcp", + properties: { + mcp_client_version: "1.0.0", + mcp_client_name: "test-client", + mcp_server_version: "1.0.0", + mcp_server_name: "test-server", + platform: "test-platform", + arch: "test-arch", + os_type: "test-os", + component: "test-component", + duration_ms: 100, + result: "success" as TelemetryResult, + category: "test-category", + }, + }, + ]; + + beforeEach(() => { + apiClient = new ApiClient({ + baseUrl: "https://api.test.com", + credentials: { + clientId: "test-client-id", + clientSecret: "test-client-secret", + }, + userAgent: "test-user-agent", + }); + + // @ts-expect-error accessing private property for testing + apiClient.getAccessToken = jest.fn().mockResolvedValue("mockToken"); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("constructor", () => { + it("should create a client with the correct configuration", () => { + expect(apiClient).toBeDefined(); + expect(apiClient.hasCredentials()).toBeDefined(); + }); + }); + + describe("listProjects", () => { + it("should return a list of projects", async () => { + const mockProjects = { + results: [ + { id: "1", name: "Project 1" }, + { id: "2", name: "Project 2" }, + ], + totalCount: 2, + }; + + const mockGet = jest.fn().mockImplementation(() => ({ + data: mockProjects, + error: null, + response: new Response(), + })); + + // @ts-expect-error accessing private property for testing + apiClient.client.GET = mockGet; + + const result = await apiClient.listProjects(); + + expect(mockGet).toHaveBeenCalledWith("/api/atlas/v2/groups", undefined); + expect(result).toEqual(mockProjects); + }); + + it("should throw an error when the API call fails", async () => { + const mockError = { + reason: "Test error", + detail: "Something went wrong", + }; + + const mockGet = jest.fn().mockImplementation(() => ({ + data: null, + error: mockError, + response: new Response(), + })); + + // @ts-expect-error accessing private property for testing + apiClient.client.GET = mockGet; + + await expect(apiClient.listProjects()).rejects.toThrow(); + }); + }); + + describe("sendEvents", () => { + it("should send events to authenticated endpoint when token is available and valid", async () => { + const mockFetch = jest.spyOn(global, "fetch"); + mockFetch.mockResolvedValueOnce(new Response(null, { status: 200 })); + + await apiClient.sendEvents(mockEvents); + + const url = new URL("api/private/v1.0/telemetry/events", "https://api.test.com"); + expect(mockFetch).toHaveBeenCalledWith(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer mockToken", + Accept: "application/json", + "User-Agent": "test-user-agent", + }, + body: JSON.stringify(mockEvents), + }); + }); + + it("should fall back to unauthenticated endpoint when token is not available via exception", async () => { + const mockFetch = jest.spyOn(global, "fetch"); + mockFetch.mockResolvedValueOnce(new Response(null, { status: 200 })); + + // @ts-expect-error accessing private property for testing + apiClient.getAccessToken = jest.fn().mockRejectedValue(new Error("No access token available")); + + await apiClient.sendEvents(mockEvents); + + const url = new URL("api/private/unauth/telemetry/events", "https://api.test.com"); + expect(mockFetch).toHaveBeenCalledWith(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "User-Agent": "test-user-agent", + }, + body: JSON.stringify(mockEvents), + }); + }); + + it("should fall back to unauthenticated endpoint when token is undefined", async () => { + const mockFetch = jest.spyOn(global, "fetch"); + mockFetch.mockResolvedValueOnce(new Response(null, { status: 200 })); + + // @ts-expect-error accessing private property for testing + apiClient.getAccessToken = jest.fn().mockReturnValueOnce(undefined); + + await apiClient.sendEvents(mockEvents); + + const url = new URL("api/private/unauth/telemetry/events", "https://api.test.com"); + expect(mockFetch).toHaveBeenCalledWith(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "User-Agent": "test-user-agent", + }, + body: JSON.stringify(mockEvents), + }); + }); + + it("should fall back to unauthenticated endpoint on 401 error", async () => { + const mockFetch = jest.spyOn(global, "fetch"); + mockFetch + .mockResolvedValueOnce(new Response(null, { status: 401 })) + .mockResolvedValueOnce(new Response(null, { status: 200 })); + + await apiClient.sendEvents(mockEvents); + + const url = new URL("api/private/unauth/telemetry/events", "https://api.test.com"); + expect(mockFetch).toHaveBeenCalledTimes(2); + expect(mockFetch).toHaveBeenLastCalledWith(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "User-Agent": "test-user-agent", + }, + body: JSON.stringify(mockEvents), + }); + }); + + it("should throw error when both authenticated and unauthenticated requests fail", async () => { + const mockFetch = jest.spyOn(global, "fetch"); + mockFetch + .mockResolvedValueOnce(new Response(null, { status: 401 })) + .mockResolvedValueOnce(new Response(null, { status: 500 })); + + const mockToken = "test-token"; + // @ts-expect-error accessing private property for testing + apiClient.getAccessToken = jest.fn().mockResolvedValue(mockToken); + + await expect(apiClient.sendEvents(mockEvents)).rejects.toThrow(); + }); + }); +}); diff --git a/tests/unit/session.test.ts b/tests/unit/session.test.ts new file mode 100644 index 00000000..44126359 --- /dev/null +++ b/tests/unit/session.test.ts @@ -0,0 +1,65 @@ +import { jest } from "@jest/globals"; +import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; +import { Session } from "../../src/session.js"; +import { config } from "../../src/config.js"; + +jest.mock("@mongosh/service-provider-node-driver"); +const MockNodeDriverServiceProvider = NodeDriverServiceProvider as jest.MockedClass; + +describe("Session", () => { + let session: Session; + beforeEach(() => { + session = new Session({ + apiClientId: "test-client-id", + apiBaseUrl: "https://api.test.com", + }); + + MockNodeDriverServiceProvider.connect = jest.fn(() => + Promise.resolve({} as unknown as NodeDriverServiceProvider) + ); + }); + + describe("connectToMongoDB", () => { + const testCases: { + connectionString: string; + expectAppName: boolean; + name: string; + }[] = [ + { + connectionString: "mongodb://localhost:27017", + expectAppName: true, + name: "db without appName", + }, + { + connectionString: "mongodb://localhost:27017?appName=CustomAppName", + expectAppName: false, + name: "db with custom appName", + }, + { + connectionString: + "mongodb+srv://test.mongodb.net/test?retryWrites=true&w=majority&appName=CustomAppName", + expectAppName: false, + name: "atlas db with custom appName", + }, + ]; + + for (const testCase of testCases) { + it(`should update connection string for ${testCase.name}`, async () => { + await session.connectToMongoDB(testCase.connectionString, config.connectOptions); + expect(session.serviceProvider).toBeDefined(); + + // eslint-disable-next-line @typescript-eslint/unbound-method + const connectMock = MockNodeDriverServiceProvider.connect as jest.Mock< + typeof NodeDriverServiceProvider.connect + >; + expect(connectMock).toHaveBeenCalledOnce(); + const connectionString = connectMock.mock.calls[0]?.[0]; + if (testCase.expectAppName) { + expect(connectionString).toContain("appName=MongoDB+MCP+Server"); + } else { + expect(connectionString).not.toContain("appName=MongoDB+MCP+Server"); + } + }); + } + }); +}); diff --git a/tests/unit/telemetry.test.ts b/tests/unit/telemetry.test.ts new file mode 100644 index 00000000..3e27f9eb --- /dev/null +++ b/tests/unit/telemetry.test.ts @@ -0,0 +1,370 @@ +import { ApiClient } from "../../src/common/atlas/apiClient.js"; +import { Session } from "../../src/session.js"; +import { Telemetry } from "../../src/telemetry/telemetry.js"; +import { BaseEvent, TelemetryResult } from "../../src/telemetry/types.js"; +import { EventCache } from "../../src/telemetry/eventCache.js"; +import { config } from "../../src/config.js"; +import { jest } from "@jest/globals"; +import logger, { LogId } from "../../src/logger.js"; +import { createHmac } from "crypto"; + +// Mock the ApiClient to avoid real API calls +jest.mock("../../src/common/atlas/apiClient.js"); +const MockApiClient = ApiClient as jest.MockedClass; + +// Mock EventCache to control and verify caching behavior +jest.mock("../../src/telemetry/eventCache.js"); +const MockEventCache = EventCache as jest.MockedClass; + +const nextTick = () => new Promise((resolve) => process.nextTick(resolve)); + +describe("Telemetry", () => { + const machineId = "test-machine-id"; + const hashedMachineId = createHmac("sha256", machineId.toUpperCase()).update("atlascli").digest("hex"); + + let mockApiClient: jest.Mocked; + let mockEventCache: jest.Mocked; + let session: Session; + let telemetry: Telemetry; + let telemetryConfig: { + eventCache: EventCache; + getRawMachineId: () => Promise; + getContainerEnv: () => Promise; + }; + + // Helper function to create properly typed test events + function createTestEvent(options?: { + result?: TelemetryResult; + component?: string; + category?: string; + command?: string; + duration_ms?: number; + }): Omit & { + properties: { + component: string; + duration_ms: number; + result: TelemetryResult; + category: string; + command: string; + }; + } { + return { + timestamp: new Date().toISOString(), + source: "mdbmcp", + properties: { + component: options?.component || "test-component", + duration_ms: options?.duration_ms || 100, + result: options?.result || "success", + category: options?.category || "test", + command: options?.command || "test-command", + }, + }; + } + + // Helper function to verify mock calls to reduce duplication + function verifyMockCalls({ + sendEventsCalls = 0, + clearEventsCalls = 0, + appendEventsCalls = 0, + sendEventsCalledWith = undefined, + appendEventsCalledWith = undefined, + }: { + sendEventsCalls?: number; + clearEventsCalls?: number; + appendEventsCalls?: number; + sendEventsCalledWith?: BaseEvent[] | undefined; + appendEventsCalledWith?: BaseEvent[] | undefined; + } = {}) { + const { calls: sendEvents } = mockApiClient.sendEvents.mock; + const { calls: clearEvents } = mockEventCache.clearEvents.mock; + const { calls: appendEvents } = mockEventCache.appendEvents.mock; + + expect(sendEvents.length).toBe(sendEventsCalls); + expect(clearEvents.length).toBe(clearEventsCalls); + expect(appendEvents.length).toBe(appendEventsCalls); + + if (sendEventsCalledWith) { + expect(sendEvents[0]?.[0]).toMatchObject(sendEventsCalledWith); + } + + if (appendEventsCalledWith) { + expect(appendEvents[0]?.[0]).toMatchObject(appendEventsCalledWith); + } + } + + beforeEach(() => { + // Reset mocks before each test + jest.clearAllMocks(); + + // Setup mocked API client + mockApiClient = new MockApiClient({ baseUrl: "" }) as jest.Mocked; + //@ts-expect-error This is a workaround + mockApiClient.sendEvents = jest.fn<() => undefined>().mockResolvedValue(undefined); + mockApiClient.hasCredentials = jest.fn<() => boolean>().mockReturnValue(true); + + // Setup mocked EventCache + mockEventCache = new MockEventCache() as jest.Mocked; + //@ts-expect-error This is a workaround + mockEventCache.getEvents = jest.fn().mockReturnValue([]); + //@ts-expect-error This is a workaround + mockEventCache.clearEvents = jest.fn().mockResolvedValue(undefined); + //@ts-expect-error This is a workaround + mockEventCache.appendEvents = jest.fn().mockResolvedValue(undefined); + //@ts-expect-error This is a workaround + MockEventCache.getInstance = jest.fn().mockReturnValue(mockEventCache); + + // Create a simplified session with our mocked API client + session = { + apiClient: mockApiClient, + sessionId: "test-session-id", + agentRunner: { name: "test-agent", version: "1.0.0" } as const, + //@ts-expect-error This is a workaround + close: jest.fn().mockResolvedValue(undefined), + //@ts-expect-error This is a workaround + setAgentRunner: jest.fn().mockResolvedValue(undefined), + } as unknown as Session; + + telemetryConfig = { + eventCache: mockEventCache, + getRawMachineId: () => Promise.resolve(machineId), + getContainerEnv: () => Promise.resolve(false), + }; + + telemetry = Telemetry.create(session, config, telemetryConfig); + + config.telemetry = "enabled"; + }); + + describe("sending events", () => { + describe("when telemetry is enabled", () => { + it("should send events successfully", async () => { + const testEvent = createTestEvent(); + + telemetry.emitEvents([testEvent]); + await nextTick(); // wait for the event to be sent + + verifyMockCalls({ + sendEventsCalls: 1, + clearEventsCalls: 1, + sendEventsCalledWith: [testEvent], + }); + }); + + it("should cache events when sending fails", async () => { + mockApiClient.sendEvents.mockRejectedValueOnce(new Error("API error")); + + const testEvent = createTestEvent(); + + telemetry.emitEvents([testEvent]); + await nextTick(); // wait for the event to be sent + + verifyMockCalls({ + sendEventsCalls: 1, + appendEventsCalls: 1, + appendEventsCalledWith: [testEvent], + }); + }); + + 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", + }); + + // Set up mock to return cached events + mockEventCache.getEvents.mockReturnValueOnce([cachedEvent]); + + telemetry.emitEvents([newEvent]); + await nextTick(); // wait for the event to be sent + + verifyMockCalls({ + sendEventsCalls: 1, + clearEventsCalls: 1, + sendEventsCalledWith: [cachedEvent, newEvent], + }); + }); + + it("should correctly add common properties to events", async () => { + // 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: hashedMachineId, + }; + + const testEvent = createTestEvent(); + + telemetry.emitEvents([testEvent]); + await nextTick(); // wait for the event to be sent + + const checkEvent = { + ...testEvent, + properties: { + ...testEvent.properties, + ...expectedProps, + }, + }; + + verifyMockCalls({ + sendEventsCalls: 1, + clearEventsCalls: 1, + sendEventsCalledWith: [checkEvent], + }); + }); + + it("should send cache new event while sending another event", async () => { + const newEvent = createTestEvent({ + command: "new-command", + component: "new-component", + }); + + const newEvent2 = createTestEvent({ + command: "new-command-2", + component: "new-component-2", + }); + + telemetry.emitEvents([newEvent]); + telemetry.emitEvents([newEvent2]); + + await nextTick(); // wait for the event to be sent + + verifyMockCalls({ + sendEventsCalls: 1, + clearEventsCalls: 1, + appendEventsCalls: 1, + sendEventsCalledWith: [newEvent], + appendEventsCalledWith: [newEvent2], + }); + }); + + describe("machine ID resolution", () => { + it("should successfully resolve the machine ID", async () => { + const testEvent = createTestEvent(); + + telemetry.emitEvents([testEvent]); + await nextTick(); // wait for the event to be sent + + const checkEvent = { + ...testEvent, + properties: { + ...testEvent.properties, + device_id: hashedMachineId, + }, + }; + + verifyMockCalls({ + sendEventsCalls: 1, + clearEventsCalls: 1, + sendEventsCalledWith: [checkEvent], + }); + }); + + it("should handle machine ID resolution failure", async () => { + const loggerSpy = jest.spyOn(logger, "debug"); + + telemetry = Telemetry.create(session, config, { + ...telemetryConfig, + getRawMachineId: () => Promise.reject(new Error("Failed to get device ID")), + }); + + const testEvent = createTestEvent(); + + telemetry.emitEvents([testEvent]); + + await nextTick(); // wait for the event to be sent + + expect(loggerSpy).toHaveBeenCalledWith( + LogId.telemetryDeviceIdFailure, + "telemetry", + "Error: Failed to get device ID" + ); + }); + + it("should timeout if machine ID resolution takes too long", () => { + const loggerSpy = jest.spyOn(logger, "debug"); + + jest.useFakeTimers(); + + telemetry = Telemetry.create(session, config, { + ...telemetryConfig, + getRawMachineId: () => new Promise(() => {}), // Never resolves + }); + + const testEvent = createTestEvent(); + + telemetry.emitEvents([testEvent]); + + jest.advanceTimersByTime(5000); + + jest.useRealTimers(); + + expect(loggerSpy).toHaveBeenCalledTimes(2); + + expect(loggerSpy).toHaveBeenNthCalledWith( + 2, + LogId.telemetryDeviceIdTimeout, + "telemetry", + "Device ID retrieval timed out" + ); + }); + }); + }); + + describe("when telemetry is disabled", () => { + beforeEach(() => { + config.telemetry = "disabled"; + }); + + afterEach(() => { + config.telemetry = "enabled"; + }); + + it("should not send events", async () => { + const testEvent = createTestEvent(); + + telemetry.emitEvents([testEvent]); + await nextTick(); // wait for the event to be sent + + verifyMockCalls({ + sendEventsCalls: 0, + }); + }); + }); + + 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"; + }); + + 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(); + + telemetry.emitEvents([testEvent]); + await nextTick(); // wait for the event to be sent + + verifyMockCalls({ + sendEventsCalls: 0, + }); + }); + }); + }); +}); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..57edf983 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "nodenext", + "moduleResolution": "nodenext", + "rootDir": "./src", + "outDir": "./dist", + "strict": true, + "strictNullChecks": true, + "noUncheckedIndexedAccess": true, + "esModuleInterop": true, + "types": ["node"], + "sourceMap": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "typeRoots": ["./node_modules/@types", "./src/types"] + }, + "include": ["src/**/*.ts"] +} diff --git a/tsconfig.jest.json b/tsconfig.jest.json index d92e8897..e3a80ccc 100644 --- a/tsconfig.jest.json +++ b/tsconfig.jest.json @@ -1,10 +1,9 @@ { - "extends": "./tsconfig.json", + "extends": "./tsconfig.build.json", "compilerOptions": { - "module": "esnext", - "target": "esnext", "isolatedModules": true, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "types": ["jest", "jest-extended"] }, "include": ["src/**/*.ts", "tests/**/*.ts"] } diff --git a/tsconfig.json b/tsconfig.json index 1fe57f10..977d46fd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,9 @@ { + "extends": "./tsconfig.build.json", "compilerOptions": { - "target": "es2020", - "module": "nodenext", - "moduleResolution": "nodenext", - "rootDir": "./src", - "outDir": "./dist", - "strict": true, - "strictNullChecks": true, - "esModuleInterop": true, - "types": ["node"], - "sourceMap": true, - "skipLibCheck": true, - "resolveJsonModule": true, - "allowSyntheticDefaultImports": true, - "typeRoots": ["./node_modules/@types", "./src/types"] + "rootDir": ".", + "types": ["jest"], + "skipLibCheck": true }, - "include": ["src/**/*.ts"] + "include": ["**/*"] }